業務需求注:債券系統,用戶創建委託並見單成交、因爲委託數量較大、見單成交修改數據庫數據較多。在此使用阻塞隊列快速返回成功與否給用戶提升用戶體驗,使用線程加快處理業務速度。
流程:用戶下單--下單驗證和委託數據生成--根據債券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); } }