Java 线程池分批调用

本文记录 Java 分批调用代码逻辑,虽然分批调用有很多种写法,但是考虑线程池提交任务执行、某个批次执行结果失败如何处理、某一批次的执行结果如何与原来的 task 对应等。考虑到这些细节,记录下分批处理的写法:

public class InvokeAllTest {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(4);

        //一组待查询的 poi, 假设1k个
        List<Long> poiNeed2queryList = new ArrayList<>();

        //拆分成 每批100个 查询
        List<List<Long>> allShardPoiList = Lists.partition(poiNeed2queryList, 100);
        List<Callable<List<String>>> tasks = new ArrayList<>();
        for (List<Long> shardPoiList : allShardPoiList) {
            Callable<List<String>> task = () -> queryName(shardPoiList);
            tasks.add(task);
        }

        try {
            //future 的顺序 与 tasks 中任务顺序保持一致
            List<Future<List<String>>> futureList = threadPool.invokeAll(tasks, 100, TimeUnit.MILLISECONDS);
            for (Future<List<String>> future : futureList) {
                try {
                    //获取到这一批次的 查询结果
                    List<String> poiNameList = future.get();
                    //后续 处理逻辑

                } catch (Exception e) {
                    //获取某一批次结果失败,做一些处理
                }
            }
        } catch (Exception e) {
            //所有批次调用失败,做一些处理
        }
    }

    //模拟根据 poiId 查 poi名称
    private static List<String> queryName(List<Long> poiList) {
        return new ArrayList<>();
    }
}
  1. guava 的 Lists.partition 能够方便地对 list 做分批
  2. 采用 invokeAll 将多个批次的查询,提交线程池任务,并发执行。并可以指定各批次的超时时间(100ms 超时, future.get时会抛出异常)。具体地:
  • 任务执行过程中出现了异常,future.get 抛出:ExecutionException
  • 任务被取消(比如 调用 future.cancel ),则 future.get 抛出:CancellationException
  • 执行该任务的线程被其它线程请求中断,则抛出:InterruptedException
  1. 线程池执行结果 futureList 中 future 的顺序和 tasks 列表中的 task 顺序是保持一致的,方便:某一批次的任务与该批次的执行结果对应起来
  2. 需要考虑某一批次执行失败了,如何处理?for 循环中 try-catch 获取某一批次的执行结果,若获取失败,执行处理逻辑
  3. 需要考虑所有批次执行失败了,如何处理?

第二种批量执行的方式:
如果某一批次的任务执行完成了,可以立即获取执行结果,并基于“执行”结果进行处理。但是无法指定每一批次的执行超时时间

    //无法指定某一批次任务的超时超时
    private static void completionServiceTest() {
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        ExecutorCompletionService<List<String>> completionService = new ExecutorCompletionService<>(threadPool);

        //一组待查询的 poi, 假设1k个
        List<Long> poiNeed2queryList = new ArrayList<>();
        //拆分成 每批100个 查询
        List<List<Long>> allShardPoiList = Lists.partition(poiNeed2queryList, 100);

        for (List<Long> shardPoiList : allShardPoiList) {
            Callable<List<String>> task = () -> queryName(shardPoiList);
            //提交 一个批次 查询任务
            completionService.submit(task);
        }

        try {
            for (int i = 0; i < allShardPoiList.size(); i++) {
                //若某一批次执行完成,能够立即获取结果. 如果所有批次都未执行完成, 则阻塞
                final Future<List<String>> future = completionService.take();
                try {
                    //在这里 future.get() 不会出现阻塞 
                    final List<String> poiNameList = future.get();
                    
                    //后续处理逻辑, 处理 poiNameList
                } catch (Exception e) {
                    //获取某一批次结果失败,做一些处理
                }
            }

        } catch (InterruptedException e) {
            //take 方法异常处理

        }
    }

这篇文章列举了几种不同的分批调用处理方式:http://www.javabyexamples.com/submit-tasks-in-batch-using-executorservice

posted @ 2023-07-20 16:22  大熊猫同学  阅读(142)  评论(0编辑  收藏  举报