好文链接
1.重大差异:
- ExecutorService
submit()
的结果是Future
, Future get() 方法的致命缺陷:如果 Future 结果没有完成,调用 get() 方法,程序会阻塞在那里,直至获取返回结果。 - CompletionService 可以做到获取最先执行完的任务结果。
2.使用方式:
executorService.submit()
后的结果需要保存在Future
中,多个提交的结果保存在List<Future>
中。executorCompletionService.submit()
的结果直接放在executorCompletionService中了,因为executorCompletionService的内部实现中有自己的队列。
使用示例
使用示例:见 “既生 ExecutorService, 何生 CompletionService?”,以下是复制过来的:
假设我们有 4 个任务(A, B, C, D)用来执行复杂的计算,每个任务的执行时间随着输入参数的不同而不同,如果将任务提交到 ExecutorService, 相信你已经可以“信手拈来”:
ExecutorService
ExecutorService executorService = Executors.newFixedThreadPool(4);
List<Future> futures = new ArrayList<Future<Integer>>();
futures.add(executorService.submit(A));
futures.add(executorService.submit(B));
futures.add(executorService.submit(C));
futures.add(executorService.submit(D));
// 遍历 Future list,通过 get() 方法获取每个 future 结果,这里的 get()会阻塞
for (Future future:futures) {
Integer result = future.get();
// 其他业务逻辑
}
ExecutorCompletionService
ExecutorService executorService = Executors.newFixedThreadPool(4);
// ExecutorCompletionService 是 CompletionService 唯一实现类
CompletionService executorCompletionService= new ExecutorCompletionService<>(executorService );
List<Future> futures = new ArrayList<Future<Integer>>();
futures.add(executorCompletionService.submit(A));
futures.add(executorCompletionService.submit(B));
futures.add(executorCompletionService.submit(C));
futures.add(executorCompletionService.submit(D));
// 遍历 Future list,通过 get() 方法获取每个 future 结果,谁先有结果取谁
for (int i=0; i<futures.size(); i++) {
Integer result = executorCompletionService.take().get();
// 其他业务逻辑
}
那么,在executorCompletionService中,使用的List<Future>
看起来并不需要,因为直接可以从 executorCompletionService.take().get();中获得结果。我们通过例子进一步验证:
关于这个例子的来源:ExecutorCompletionService的使用和实现
package threads.completeService;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class CallableTask implements Callable<Integer> {
/**
* 休眠时间
*/
private int sleepSeconds;
/**
* 返回的值
*/
private int returnValue;
public CallableTask(int sleepSeconds, int returnValue) {
this.sleepSeconds = sleepSeconds;
this.returnValue = returnValue;
}
@Override
public Integer call() throws Exception {
System.out.println("begin to execute.");
TimeUnit.SECONDS.sleep(sleepSeconds);
System.out.println("end to execute.");
return returnValue;
}
}
public class Main {
public static void main(String[] args) {
int taskSize = 5;
ExecutorService executor = Executors.newFixedThreadPool(taskSize);
// 构建完成服务
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor);
int sleep = 5; // 睡眠时间,单位是秒,不是毫秒
for (int i = 1; i <= taskSize; i++) {
int value = i; // 返回结果
// 向线程池提交任务
completionService.submit(new CallableTask(sleep, value));//返回结果类型FutureTask
}
// 按照完成顺序,打印结果
for (int i = 0; i < taskSize; i++) {
try {
System.out.println(completionService.take().get());// 阻塞,知道有任务完成可以获取结果
// System.out.println(completionService.poll());//poll直接返回,不阻塞。但是没有完成的任务则返回null
// System.out.println(completionService.poll(5, TimeUnit.SECONDS));//阻塞等待指定时间,如果有完成结果返回,没有的直接返回null // completionService.submit(new RunnableTask(),2);//completionService提交Runnable任务是无法获取结果的
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// 所有任务已经完成,关闭线程池
System.out.println("执行完毕....");
executor.shutdown();
}
}
打印的结果是:
end to execute.
end to execute.
end to execute.
end to execute.
end to execute.
4
5
2
3
1
由于每个线程都sleep5s,谁先执行完具有随机性。我们将结果放在List<Future<Integer>>
中,也是可以获取的:
@Test
public void test2() throws Exception{
int taskSize = 5;
ExecutorService executor = Executors.newFixedThreadPool(taskSize);
// 构建完成服务
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
int sleep = 5; // 睡眠时间,单位是秒,不是毫秒
for (int i = 1; i <= taskSize; i++) {
int value = i; // 返回结果
// 向线程池提交任务
futures.add(completionService.submit(new CallableTask(sleep, value)));//返回结果类型FutureTask
}
// 按照完成顺序,打印结果
for (int i = 0; i < taskSize; i++) {
try {
System.out.println(completionService.take().get());// 阻塞,知道有任务完成可以获取结果
// System.out.println(completionService.poll());//poll直接返回,不阻塞。但是没有完成的任务则返回null
// System.out.println(completionService.poll(5, TimeUnit.SECONDS));//阻塞等待指定时间,如果有完成结果返回,没有的直接返回null // completionService.submit(new RunnableTask(),2);//completionService提交Runnable任务是无法获取结果的
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
for (Future future:futures) {
System.out.println("futures中取:"+future.get());
}
// 所有任务已经完成,关闭线程池
System.out.println("执行完毕....");
executor.shutdown();
}
结果如下,从队列中取是按序的:
begin to execute.
begin to execute.
begin to execute.
begin to execute.
begin to execute.
end to execute.
end to execute.
end to execute.
end to execute.
3
4
2
1
end to execute.
5
futures中取:1
futures中取:2
futures中取:3
futures中取:4
futures中取:5
执行完毕....
示例结果表明:ExecutorCompletionService的结果可以直接从ExecutorCompletionService中取出,也可以放到List<Future>
中,放到list中可以按序取出。