业务需求注:债券系统,用户创建委托并见单成交、因为委托数量较大、见单成交修改数据库数据较多。在此使用阻塞队列快速返回成功与否给用户提升用户体验,使用线程加快处理业务速度。
流程:用户下单--下单验证和委托数据生成--根据债券code判断是否存在此code并放入对应的阻塞队列--存在此code直接放入阻塞队列,不存在则放入阻塞队列同时启动线程--启动的线程轮训take阻塞队列中某一code的数据。(相当于一个code对应一个线程。)以下是代码:
1、声明阻塞队列:
@Service public class ConcurrentSkipListMapFactory { //保存新生委托队列 key:bondCode,value是阻塞队列 public static HashMap<String,LinkedBlockingQueue<BondEntrust>> linkedMap = new HashMap<String,LinkedBlockingQueue<BondEntrust>>();}
2、委托下单是判断阻塞队列中是否含有code,含有code直接放入阻塞队列,不含code,放入阻塞队列同时启动一个新线程。注意:必须需要入参bondCode(用途:只处理这一个债券的业务),
try { if (null == ConcurrentSkipListMapFactory.linkedMap.get(bondCode)){//不含有code baseStrategy.sightDeal(bondCode);//启动一个线程 LinkedBlockingQueue<BondEntrust> queue = new LinkedBlockingQueue<BondEntrust>(); queue.put(bondEntrust); ConcurrentSkipListMapFactory.linkedMap.put(bondCode,queue);//放入全局阻塞队列map中 }else { ConcurrentSkipListMapFactory.linkedMap.get(bondCode).put(bondEntrust);//直接放入map中 } } catch (InterruptedException e) { e.printStackTrace(); return renderError("新生委托存放数据失败"); }
3、创建线程池和生成具体线程的类;一共三个类,注意:必须需要入参bondCode(用途:只处理这一个债券的业务)
package com.bond.match.Thread.strategy;
import com.bond.match.Thread.worker.*; import org.springframework.stereotype.Service; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 线程任务调度类 * Date: 2018/1/9 0009 * Time: 10:40 * To change this template use File | Settings | File Templates. */ @Service public class BaseStrategy { /** * Executors 工厂方法 * Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收) * Executors.newFixedThreadPool(int)(固定大小线程池) * Executors.newSingleThreadExecutor()(单个后台线程) */ public static ExecutorService SETTLEPOOL = Executors.newCachedThreadPool(); public static ExecutorService SINGLEPOOL = Executors.newSingleThreadExecutor(); /** * 产生多个线程:见单成交使用 * @param bondCode */ public void sightDeal(String bondCode){ SETTLEPOOL.execute(new SightDealWorler(bondCode)); } }
package com.bond.match.Thread.worker; import com.bond.match.BondDeal.service.BondDealService; import com.bond.match.Queue.ConcurrentSkipListMapFactory; import org.apache.log4j.Logger; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.concurrent.LinkedBlockingQueue; /** * 线程工作类 * Date: 2018/1/9 0009 * Time: 10:43 * To change this template use File | Settings | File Templates. */ @Component() @Scope("prototype") public class SightDealWorler extends MatchWorker{ private Logger logger = Logger.getLogger(getClass()); private String bondCode = ""; public SightDealWorler(String str){ this.bondCode=str; } protected void execute(){ logger.info("当前线程名称:"+Thread.currentThread().getName()+"债券代码:"+bondCode); //因线程安全,不能使用Spring注入方式,所以得new一个对象 //若非得需要使用Spring注入,实现Spring的Applicationcontext中getBean方法 new BondDealService().sightDealTake(bondCode); } }
package com.bond.match.Thread.worker; import org.apache.log4j.Logger; /** * 线程抽象类 * Date: 2018/1/9 0009 * Time: 10:34 * To change this template use File | Settings | File Templates. */ public abstract class MatchWorker extends Thread { private Logger logger = Logger.getLogger(getClass()); public void run() { before(); execute(); after(); } protected abstract void execute(); protected void before() { logger.debug("开始执行成交撮合"); } protected void after() { logger.debug("结束执行成交撮合"); } }
4、线程中轮训执行对应业务
/** * 启动每个bondCode的获取线程 * @param bondCode */ public void sightDealTake(String bondCode){ try { BondEntrust bondEntrust = new BondEntrust(); while (true){ bondEntrust = ConcurrentSkipListMapFactory.linkedMap.get(bondCode).take(); logger.info("线程获取队列queue数据take:--bondCode"+bondCode); if (null != bondEntrust){ if (8==bondEntrust.getEntrustState()){//撤单委托 ApplicationContextProvider.getBean(BondEntrustService.class).repealBondEntrust(bondEntrust,true); }else {//新生委托,需见单成交 this.sightDeal(bondEntrust); } } } } catch (Exception e) { e.printStackTrace(); logger.info("线程异常、重启债券线程:--bondCode"+bondCode); ApplicationContextProvider.getBean(BaseStrategy.class).sightDeal(bondCode); } }