ExecutorCompletionService的使用

138 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

一、ExecutorCompletionService使用场景

通常在执行一批需要返回结果的任务时,我们可以使用线程池来提高程序运行效率,通过线程池的 submit(Callable task) 不断提交异步任务,并将 Future 保存下来,之后遍历Future,调用 get() 方法获取结果。虽然任务都是异步执行的,但是 get Future 结果是阻塞的。

例如第一个 future 需要计算5s才能返回结果,但是其他 future 不到1s就会返回计算结果,需要等待第一个 future 结果返回才能 get 到其他 future 的结果,这就白白浪费了很多时间。此时就可以使用 ExecutorCompletionService,它实现了 CompletionService 接口。它的内部有一个先进先出的阻塞队列,用于保存执行结束的 future,通过调用 ExecutorCompletionService 的 take 方法,就可以获取到第一个已经执行完成的 Future,之后再调用 future 的 get 方法,就可以获取到最终的结果。

二、CompletionService和ExecutorCompletionService的关联

1、ExecutorCompletionService类是CompletionService接口的实现
  • ExecutorCompletionService内部管理者一个已完成任务的阻塞队列;

  • ExecutorCompletionService引用了一个Executor, 用来执行任务;

  • submit()方法最终会委托给内部的executor去执行任务;

  • take/poll方法的工作都委托给内部的已完成任务阻塞队列

  • 如果阻塞队列中有已完成的任务, take方法就返回任务的结果, 否则阻塞等待任务完成;

  • poll与take方法不同, poll有两个版本:

    • 无参的poll方法 --- 如果完成队列中有数据就返回, 否则返回null
    • 有参数的poll方法 --- 如果完成队列中有数据就直接返回, 否则等待指定的时间, 到时间后如果还是没有数据就返回null
    • ExecutorCompletionService主要用与管理异步任务 (有结果的任务, 任务完成后要处理结果)
2、CompletionService接口定义了一组任务管理接口
  • submit() - 提交任务

    Future submit(Callable task); submit 用于提交一个 Callable 对象,用于提交一个可以获得结果的线程任务

  • take() - 获取任务结果

    Future take() throws InterruptedException; take 用于取出最新的线程执行结果,注意这里是阻塞的

  • poll() - 获取任务结果

    Future poll(); take 用于取出最新的线程执行结果,是非阻塞的,如果没有结果就返回 null

    Future poll(long timeout, TimeUnit unit) throws InterruptedException; 同上,只是加了一个超时时间

3、关于CompletionService和ExecutorCompletionService的类图如下:

image.png

三、ExecutorCompletionService的应用

ExecutorCompletionService 实现原理,其实很简单,内部维护了一个阻塞队列,提交的任务,先执行完的先进入队列,所以你通过 poll 或 take 获得的肯定是最先执行完的任务结果。

用处:多个线程,先执行完的进阻塞队列,然后可以按执行顺序获取结果

demo实例

public class TaskHandler {


    private static final String THREAD_FACTORY_NAME_FORMAT = "-pool-%d";
    private static final int CORE_POOL_SIZE = 5;
    private static final int MAXIMUM_POOL_SIZE = 10;
    private static final int KEEP_ALIVE_TIME = 60;
    private static final int LINKED_BLOCKING_QUEUE_CAPACITY = 1000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //创建线程工厂
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("ExecutorCompletionService-test" + THREAD_FACTORY_NAME_FORMAT)
                .build();

        //创建线程池
        Executor executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(LINKED_BLOCKING_QUEUE_CAPACITY),
                threadFactory,
                new ThreadPoolExecutor.CallerRunsPolicy());
        //创建CompletionService执行对象并制定使用自定义线程池
        CompletionService<HandlerResult> completionService = new ExecutorCompletionService(executor);

        //提交五个线程任务
        for (int i = 1; i <= 5; i++) {
            completionService.submit(new Task("CompletionService-task" + i, i));
        }

        System.out.println("print result");

        //take获取最先执行完成结果
        for (int i = 1; i <= 5; i++) {
            Future<HandlerResult> future = completionService.take();
            System.out.println(future.get());
        }

        System.exit(0);
    }

    public static class Task implements Callable<HandlerResult> {

        private String taskName;
        private long sleepSeconds;

        Task(String taskName, long sleepSeconds) {
            this.taskName = taskName;
            this.sleepSeconds = sleepSeconds;
        }

        @Override
        public HandlerResult call() throws Exception {
            //Thread.sleep(sleepSeconds * 1000);
            return new HandlerResult(Thread.currentThread().getName() + ": " + taskName + " run end, time = " + ZonedDateTime.now());
        }
    }

}
print result

HandlerResult(result=ExecutorCompletionService-test-pool-1: CompletionService-task2 run end, time = 2022-08-10T22:48:07.240+08:00[Asia/Shanghai])
HandlerResult(result=ExecutorCompletionService-test-pool-2: CompletionService-task3 run end, time = 2022-08-10T22:48:07.240+08:00[Asia/Shanghai])
HandlerResult(result=ExecutorCompletionService-test-pool-0: CompletionService-task1 run end, time = 2022-08-10T22:48:07.240+08:00[Asia/Shanghai])
HandlerResult(result=ExecutorCompletionService-test-pool-4: CompletionService-task5 run end, time = 2022-08-10T22:48:07.240+08:00[Asia/Shanghai])
HandlerResult(result=ExecutorCompletionService-test-pool-3: CompletionService-task4 run end, time = 2022-08-10T22:48:07.240+08:00[Asia/Shanghai])