SpringBoot使用线程池实现异步批量处理任务
模拟批处理大量数据
@Slf4j
@Component
public class TestFutureService {
@Autowired
private TestFutureServiceImpl testFutureServiceImpl;
/**
* 通常多线程的应用不是为了提高运行效率,而是为了提高资源使用效率(单核CPU不行,反而降低),还可以实现异步调用。
*
* 单核CPU同一时间只能处理一个线程(因为一个CPU一次只能执行一条指令),但速度非常快,消除阻塞,造成并行的假象(并发:交替轮流使用资源)
* 多核CPU同一时间可以处理多个线程,每个核心处理一个线程。(并行:分工同时处理多个任务)
*/
@PostConstruct
public void run(){
runTask();
}
public void runTask() {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
log.info("开始执行批量处理任务...");
List<String> dataList = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
dataList.add(IdUtils.fastSimpleUUID());
}
// 每个线程处理多少条数据
int capacity = 1000;
// 计算得出所需的任务数量
int count = NumberUtil.round(NumberUtil.div(dataList.size(), capacity), 0, RoundingMode.UP).intValue();
CountDownLatch latch = new CountDownLatch(count);
// 线程返回的结果集
List<Future<List<String>>> futureList = new ArrayList<>();
List<String> subList = new ArrayList<>(capacity);
int k = 1;
for (String id : dataList) {
if (subList.size() >= capacity) {
futureList.add(testFutureServiceImpl.doTask(subList, latch, k++));
subList = new ArrayList<>(capacity);
}
subList.add(id);
}
if(subList.size() > 0){
futureList.add(testFutureServiceImpl.doTask(subList, latch, k));
}
log.info("任务个数:" + count +"|"+ futureList.size());
try {
// 方式一:使用CountDownLatch计数器,等待所有任务执行完成
latch.await();
for (Future<List<String>> future : futureList) {
List<String> pList = future.get();
System.out.println("返回结果:" + pList.size());
}
// 方式二:使用轮循方式检查异步任务是否执行完成,特点:不需要计数器
/**
boolean bol = true;
while (bol){
for (Future<List<String>> future : futureList) {
if(future.isDone()){
bol = false;
} else {
bol = true;
break;
}
}
ThreadUtil.sleep(500);
}
*/
log.info("任务完成!");
} catch (InterruptedException e) {
log.error("处理线程中断异常", e);
} catch (ExecutionException e) {
log.error("处理执行异常", e);
} catch (Exception e) {
log.error("其它异常", e);
}
stopWatch.stop();
log.info("任务执行耗时(秒):" + stopWatch.getTotalTimeSeconds());
}
}
定义使用线程池异步处理实现类
@Slf4j
@Component
public class TestFutureServiceImpl {
// 需要自定义threadPoolTaskExecutor线程池配置,自行实现
@Async("threadPoolTaskExecutor")
public Future<List<String>> doTask(List<String> list, CountDownLatch latch, int k) {
log.info("开始执行任务:" + k + "|" + list.size());
ThreadUtil.sleep(1, TimeUnit.SECONDS);
latch.countDown();
return new AsyncResult<>(list);
}
}
模拟异步处理任务队列
@Slf4j
@Component
public class CheckModelRunStatusTask {
@Autowired
private RedisService redisService;
/**
* 后台检查异步任务是否执行完成,实时修改结果状态
*/
private Lock lock = new ReentrantLock();
@Async("threadPoolTaskExecutor")
public void checkModelStatus(String modelId, IProjectModelService projectModelService){
try {
// 往Set集合中新增任务
redisService.addCacheSet(RedisConstants.TEST_TASK, modelId);
lock.lock();
boolean status = Convert.toBool(redisService.getCacheObject(RedisConstants.STATUS), false);
log.info("接收到新的模型运行状态检查任务:{}", modelId);
if(status){
log.info("任务正在进行中...");
return;
}
log.info("开始执行检查模型运行状态任务!");
redisService.setCacheObject(RedisConstants.STATUS, true);
} finally {
lock.unlock();
}
while (true) {
try {
Set<String> keys = redisService.getCacheSet(RedisConstants.TEST_TASK);
if (!keys.isEmpty()) {
List<String> finishedIds = new ArrayList<>();
for (String id : keys) {
JSONObject obj = projectModelService.showTestStatus(id);
if(obj != null){
int status = obj.getInt("status");
if(status > 1){
finishedIds.add(id);
}
}
}
if(finishedIds.size() > 0){
redisService.removeSet(RedisConstants.TEST_TASK, finishedIds.stream().toArray(String[]::new));
}
} else {
log.info("队列中已没有任何检查任务,停止校验!!!");
redisService.setCacheObject(RedisConstants.STATUS, false);
break;
}
} catch (Exception e) {
log.error("检查任务异常:" + e.getMessage());
}
ThreadUtil.sleep(1000);
}
}
}