业务场景
当系统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缓存队列中,在任务执行完后会检查对列中是否有消息,有的话会再次执行不会有丢失的信息,可以通过本地大量数据多起几个实例验证此现象.