携手创作,共同成长!这是我参与「掘金日新计划 · 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的类图如下:
三、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])