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);
        }
    }
}
posted @ 2024-07-08 10:51  盗梦笔记  阅读(190)  评论(0编辑  收藏  举报