场景:在一个方法中,需要解析一个List对象集合,list长度1000+,单线程同步处理执行缓慢,故而使用多线程方式分批处理。
步骤如下:
1.待处理集合
//待处理集合:
List<Object> list = new ArrayList<Object>;
//调用partition方法,将list分割为10个为一组
List<List<PartitionModel>> partitionList = Lists.partition(list , 10);
int syncSize = partitionList.size();
//使用 CountDownLatch 设置计数器
CountDownLatch threadLatch = new CountDownLatch(syncSize);
//这两个容器作用是存储子线程执行结果
BlockingDeque<Boolean> resultList = new LinkedBlockingDeque<>(syncSize);
List<Future<Boolean>> futures = Lists.newArrayList();
//创建回滚控制容器
RollBack rollBack = new RollBack(false);
//创建子线程返回提示语容器
List<ResultMsg> resultMsgs = new LinkedList<>();
// 设置循环结束关键字
while (syncSize > 0) {
List<PartitionModel> models = partitionList.get(syncSize - 1);
LOGGER.info("开始第{}批次循环,本次执行的列表:{}", syncSize,
invoiceModels));
Future<Boolean> booleanFuture =
accountingHandler.link(threadLatch, resultList, inputVo, invoiceModels, resultMsgs, newInputVo);
LOGGER.info("本次执行结束,批次号:{}", syncSize);
futures.add(booleanFuture);
syncSize--;
}
// 设置超时时间为1分钟,超出执行时间中断程序并回滚
try {
// 等待所有子线程执行完毕
boolean await = threadLatch.await(60, TimeUnit.SECONDS);
if (!await) {
rollBack.setRollBack(true);
} else {
// 查看执行情况,如果有存在需要回滚的线程,则全部回滚
for (int i = 0; i < partitionList.size(); i++) {
Boolean result = resultList.take();
if (result) {
/** 有线程执行异常,需要回滚子线程. */
rollBack.setRollBack(true);
}
}
}
} catch (InterruptedException e) {
LOGGER.error("等待所有子线程执行完毕时,出现异常");
rollBack.setRollBack(true);
}
//后面根据 futures 及 rollBack.getRollBack() 可再次判断子线程执行情况,以处理后续逻辑
其中 accountingDocHandler.link()方法如下:
@Async("accountingLinkThreadPool")
public Future<Boolean> link(CountDownLatch threadLatch,
BlockingDeque<Boolean> resultList,
...
) {
//方法体
}
整体做法的优点在于可灵活控制每次处理的集合长度,及线程数量可控,可根据自己的业务场景酌情修改。。
需注意的一点是当有子线程执行失败时,回滚逻辑的处理。