業務場景
當系統A不關心繫統B執行的結果時,建議使用異步多線程的形式通知B系統處理,以下多線程的案例就是使用線程池和隊列的完成異步通知B系統的處理,廢話不多說,上代碼:
源碼:
/**
* com.hshc.threadpool.ThreadPoolManager.java
* Copyright 2017 Lifangyu, Inc. All rights reserved.
* PROPRIETARY/CONFIDENTIAL.Use is subject to license terms.
*/
package com.hshc.threadpool;
import lombok.extern.slf4j.Slf4j;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.*;
/**
* Desc:線程池管理器
*
* @author lifangyu
* @date 2017/12/8.
*/
@Slf4j
public class ThreadPoolManager {
private static ThreadPoolManager instance;
/**
* 線程池維護線程的最少數量
*/
private static int COREPOOLSIZE = 4;
/**
* 線程池維護線程的最大數量
*/
private static int MAXPOOLSIZE = 10;
/**
* 線程池維護線程所允許的空閒時間
*/
private static int KEEPALIVETIME = 0;
/**
* 線程池所使用的緩衝隊列大小
*/
private static int WORKQUEUESIZE = 10;
/**
* 消息緩衝隊列
*/
Queue<T> msgQueue = new LinkedList<>();
/**
* 非單例帶參實例,重新創建一個實例[不推薦是使用]
*
* @param corepoolsize
* @param maxpoolsize
* @param keepalivetime
* @param workqueuesize
* @return ThreadPoolManager
* @author lifangyu
*/
public static ThreadPoolManager newInstance(int corepoolsize, int maxpoolsize, int keepalivetime, int workqueuesize) {
COREPOOLSIZE = corepoolsize;
MAXPOOLSIZE = maxpoolsize;
KEEPALIVETIME = keepalivetime;
WORKQUEUESIZE = workqueuesize;
instance = new ThreadPoolManager();
return instance;
}
/**
* 單例模式[推薦使用]
*
* @return ThreadPoolManager
* @author lifangyu
*/
public static ThreadPoolManager getInstance() {
if (instance == null) {
synchronized (new Object()) {
if (instance == null) {
instance = new ThreadPoolManager();
}
}
}
return instance;
}
/**
* 訪問消息緩存的調度線程[很重要]
* 查看是否有待定請求,如果有,則創建一個新的AccessDBThread,並添加到線程池中
*/
final Runnable accessBufferThread = new Runnable() {
@Override
public void run() {
if (hasMoreAcquire()) {
T vo = (T) msgQueue.poll();
Runnable task = new CallBackThread(vo);
threadPool.execute(task);
}
}
};
final RejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.info("線程ID:{} execute thread [{}]消息放入隊列中重新等待執行", Thread.currentThread().getId(), ((CallBackThread) r).getVo().toString());
msgQueue.offer(((CallBackThread<T>) r).getVo());
}
};
/**
* 管理數據庫訪問的線程池
*/
@SuppressWarnings({"rawtypes", "unchecked"})
final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(COREPOOLSIZE, MAXPOOLSIZE, KEEPALIVETIME,
TimeUnit.SECONDS, new ArrayBlockingQueue(WORKQUEUESIZE), this.handler);
/**
* 調度線程池
*/
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(100);
@SuppressWarnings("rawtypes")
final ScheduledFuture taskHandler = scheduler.scheduleAtFixedRate(accessBufferThread, 0, 1, TimeUnit.SECONDS);
private boolean hasMoreAcquire() {
return !msgQueue.isEmpty();
}
public void noticeCallBack(String url,T vo) {
Runnable task = new CallBackThread<T>(url,vo);
threadPool.execute(task);
}
}
/**
* com.hshc.threadpool.CallBackThread.java
* Copyright 2017 Lifangyu, Inc. All rights reserved.
* HuaShengHaoChe PROPRIETARY/CONFIDENTIAL.Use is subject to license terms.
*/
package com.hshc.threadpool;
import com.hshc.common.utils.PropertiesUtil;
import com.hshc.common.utils.RestHttpUtil;
import com.hshc.wh.eas.domain.EasVoucherInstorageRequestVo;
import com.hshc.wh.eas.domain.EasVoucherMaterialRequestVo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* Desc:TODO
*
* @author lifangyu
* @date 2017/12/8.
*/
@Slf4j
@Data
public class CallBackThread implements Runnable {
private String url;
private T vo;
public CallBackThread(T vo) {
if (vo instanceof EasVoucherMaterialRequestVo) {
this.url = PropertiesUtil.getConfigValueByKey("hshc.whapi.eas.material.url");
} else if (vo instanceof EasVoucherInstorageRequestVo) {
this.url = PropertiesUtil.getConfigValueByKey("hshc.whapi.eas.purchase.url");
}
log.info("CallBackThread.url:{}", url);
this.vo = vo;
}
public CallBackThread(String url, T vo) {
this.url = url;
this.vo = vo;
}
@Override
public void run() {
log.info("線程ID:{} execute callbackFeignClient.recharge start [url:{},param:{}] ", Thread.currentThread().getId(), url, vo.toString());
// 需要異步執行的業務邏輯實現
String result = RestHttpUtil.sendPost(url, vo);
log.info("線程ID:{} execute callbackFeignClient.recharge end [result:{}] ", Thread.currentThread().getId(), vo.toString(), result);
}
}
說明
1.使用的jar:lombok;
2.消息緩存隊列很重要:Queue<T> msgQueue,如果由於資源競爭線程執行失敗,會進入到msgQueue緩存隊列中,在任務執行完後會檢查對列中是否有消息,有的話會再次執行不會有丟失的信息,可以通過本地大量數據多起幾個實例驗證此現象.