业精于勤荒于嬉,写文章练习表达能力,写代码练习基本工。
练习目标:
掌握 Java 8 新增的CompletionService的优点,用法和使用场景。
练习题目:
执行1000个任务,顺序获取结果,为了体现CompletionService的优点,这里同时用ExecutorService作为对比。
代码 – 子任务
//总共1000个任务
private static final int MAX_NUM = 1000;
//使用Callable,适用于需要返回值的场景。
public static class MyCallable implements Callable<Integer>{
int num;
public MyCallable(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
Thread.sleep(3000);//保证测试公平性,统一耗时3秒。
// System.out.println("第"+num+"个任务已完成!");
return num;
}
}
代码 – ExecutorService普通线程池执行
public static void testExecutorService() throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(MAX_NUM);
try {
List<Future> futureList = new ArrayList<>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < MAX_NUM; i++) {
futureList.add(executor.submit(new MyCallable(i)));
}
for (int i = 0; i < MAX_NUM; i++) {
futureList.get(i).get();
// System.out.println("第:"+(futureList.get(i).get()) +"个任务返回");
}
System.out.println("耗时:"+(System.currentTimeMillis() - startTime) +"毫秒");
}finally {
executor.shutdown();
executor.awaitTermination(1,TimeUnit.MINUTES);
}
}
代码 – ExecutorCompletionService线程池执行
public static void testCompletionService() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(MAX_NUM);
CompletionService completionService = new ExecutorCompletionService(executor);
try {
long startTime = System.currentTimeMillis();
for (int i = 0; i < MAX_NUM; i++) {
completionService.submit(new MyCallable(i));
}
for (int i = 0; i < MAX_NUM; i++) {
// System.out.println("第:"+(completionService.take().get()) +"个任务返回");
completionService.take().get();
}
System.out.println("耗时:"+(System.currentTimeMillis() - startTime) +"毫秒");
}finally {
executor.shutdown();
executor.awaitTermination(1,TimeUnit.MINUTES);
}
}
代码 – main
public static void main(String[] args) throws ExecutionException, InterruptedException {
testExecutorService();
testCompletionService();
}
耗时:3229毫秒
耗时:3056毫秒
结论
此场景下,使用ExecutorCompletionService会有优势
原因
- 子任务返回的furture,调用get后,如果子任务没有完成,会阻塞子任务直到结束,如果已经完成,直接返回结果。
- ExecutorCompletionService维护了一个队列,采用“先完成先入列”的思想,在调用take后,获取的都是已经结束的任务。
- 普通线程池在顺序调取furture的get方法获取结果时,遍历到的子任务可能没结束,会阻塞等待结果。
- ExecutorCompletionService在这一个场景,省去了3的时间。