场景描述
在系统中经常要用到多线程的来提高系统的运行效率,在子线程的执行过程中,如何判断其他线程的是否执行成功?如果其中一个子线程处理失败,其他的子线程如何回滚?
CountDownLatch简介
CountDownLatch 是多线程控制的一种工具,这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch 定义了一个计数器,和一个阻塞队列,await()会使当前线程一直阻塞, countDown()会使计数减1,当计数器的值递减为0之前,阻塞队列里面的线程处于挂起状态,当计数器递减到0时会唤醒阻塞队列所有线程,这里的计数器是一个标志,可以表示一个任务一个线程,也可以表示一个倒计时器,CountDownLatch可以解决那些一个或者多个线程在执行之前必须依赖于某些必要的前提业务先执行的场景。
设计思路
创建主线程计数 CountDownLatch mainLatch;
子线程计数 CountDownLatch threadLatch;
子线程执行成功/失败标识 AtomicBoolean runType 默认true;
1.在创建子线程之前,计算子线程的创建数量 threadLatch;子线程创建完成之后,在主线程中通过 threadLatch.await() ,等待子线程全部执行完毕;
2.子线程执行业务逻辑处理完成之后 调用计数 threadLatch.countDown(),使子线程计数减1;如果业务逻辑处理失败则修改 runType = false;
3.子线成处理成功之后,通过 mainLatch…await(),等待主线程的执行结果;
4.在主线程中 threadLatch.await() 监听子线程全部执行完成后,调用 mainLatch.countDown() ,激活所有等待的子线程;
5.子线程中判断 AtomicBoolean runType,如果runType == false,说明其存在执行失败的子线程,当前线程进行事务回滚。
ExecutorService executorService = Executors.newFixedThreadPool(10);
@GetMapping("/threadGet")
public List<PollingCardInfo> threadGet(){
int count = 4;//子线程数量不能大于 最大线程数,如果大于最大线程数,会导致子线程的countDownLatch不能清零
CountDownLatch threadLatch = new CountDownLatch(count);//子线程计数
CountDownLatch mainLatch = new CountDownLatch(1);//主线程
AtomicBoolean runType = new AtomicBoolean(true);
List<Future<List<PollingCardInfo>>> futureList = new ArrayList<Future<List<PollingCardInfo>>>();//各个线程处理结果集
for(int a = 0 ; a < count ; a ++){
PollingCardInfo cardInfo = new PollingCardInfo();
cardInfo.setNetworkStatus(a+"");
LatestStockSelectThread LatestStockSelectThread = new LatestStockSelectThread(threadLatch, mainLatch, runType, cardInfo,pollingCardInfoService);
Future<List<PollingCardInfo>> future = executorService.submit(LatestStockSelectThread);
futureList.add(future);
}
try {
log.info("等待处理结果");
if( !threadLatch.await(30, TimeUnit.SECONDS) ){
log.info("处理超时");
return null;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
List<PollingCardInfo> list = new ArrayList<>();
try {
if(!runType.get()){
log.info("存在执行失败的子线程");
}
//主线程执行完成
mainLatch.countDown();
if(runType.get()){
//执行成功 遍历获取处理结果
for(Future<List<PollingCardInfo>> future : futureList) {
list.addAll(future.get());
}
}else{
//执行失败 事务回滚
return null;
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return list;
}
public class LatestStockSelectThread implements Callable<List<PollingCardInfo>> {
private IPollingCardInfoService pollingCardInfoService;
/** 子线程 */
private CountDownLatch threadLatch;
/** 主线程 */
private CountDownLatch mainLatch;
/** 运行结果 */
private AtomicBoolean runType;
private PollingCardInfo cardInfo;
public LatestStockSelectThread(CountDownLatch threadLatch,CountDownLatch mainLatch,AtomicBoolean runType, PollingCardInfo cardInfo, IPollingCardInfoService pollingCardInfoService ){
this.threadLatch = threadLatch;
this.mainLatch = mainLatch;
this.runType = runType;
this.cardInfo = cardInfo;
this.pollingCardInfoService = pollingCardInfoService;
}
@Override
public List<PollingCardInfo> call() {
return pollingCardInfoService.getThreadCardInfoList(cardInfo,threadLatch,mainLatch,runType);
}
}
@Override
public List<PollingCardInfo> getThreadCardInfoList(PollingCardInfo cardInfo, CountDownLatch threadLatch, CountDownLatch mainLatch, AtomicBoolean runType) {
List<PollingCardInfo> list = new ArrayList<>();
if("5".equals(cardInfo.getNetworkStatus())){
log.info("参数异常:{}",cardInfo.getNetworkStatus());
runType.set(false);
threadLatch.countDown();
return list;
}
list = pollingCardInfoMapper.selectPollingCardInfoList(cardInfo);
//子线程执行完成
threadLatch.countDown();
log.info("处理完成:{}",cardInfo.getNetworkStatus());
try {
if(!mainLatch.await(30, TimeUnit.SECONDS)){
throw new CustomException("主线程超时,子线程事务回滚");
}
if(!runType.get()){
log.info("其他子线程执行失败,子线程事务回滚:{}",cardInfo.getNetworkStatus());
throw new CustomException("其他子线程执行失败,子线程事务回滚");
}else{
log.info("处理完成,正常提交事务:{}",cardInfo.getNetworkStatus());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return list;
}