1、不使用ExecutorCompletionService获取线程池异步执行结果的实现方式
测试代码:
public static void main(String[] args) throws ExecutionException, InterruptedException {
//不使用ExecutorCompletionService交给线程池执行异步任务,并阻塞获取执行结果的实现
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("batchCreate-%d").build();
ExecutorService exec = Executors.newFixedThreadPool(20, threadFactory);
List<Future<String>> futureList = Lists.newArrayList();
long currentTimeMillis = System.currentTimeMillis();
for (int i = 1; i <= 10; i++) {
int finalI = i;
Future<String> future = exec.submit(() -> {
Thread.sleep(1000L * (11 - finalI));
return Thread.currentThread().getName() + " : " + finalI;
});
futureList.add(future);
}
for (Future<String> future : futureList) {
String result = future.get();
System.out.println("阻塞时长:" + (System.currentTimeMillis() - currentTimeMillis) + " result:" + result);
}
exec.shutdown();
System.out.println("********************finished:" + (System.currentTimeMillis() - currentTimeMillis));
System.out.println();
}
执行结果:
结果分析:
以上代码中提交的任务,模拟的是一种最坏的任务耗时情况,从10秒~1秒,从执行结果看,是阻塞了10秒,也就是阻塞到执行时间最长的任务执行完成后,才能拿到首次执行结果,即碰到某个耗时慢的任务后,会等到此任务执行完成后,才继续获取后续任务的执行结果;
2、使用ExecutorCompletionService获取执行结果的方式
1)使用 Future 接收结果的方式,得到的效果和 1中的效果一样
代码如下:
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadFactory threadFactory1 = new ThreadFactoryBuilder().setNameFormat("batchCreate22222-%d").build();
ExecutorService exec2 = Executors.newFixedThreadPool(20, threadFactory1);
CompletionService<String> completionService = new ExecutorCompletionService<>(exec2);
long currentTimeMillis1 = System.currentTimeMillis();
List<Future<String>> futureList = Lists.newArrayList();
for (int i = 1; i <= 10; i++) {
int finalI = i;
Future<String> stringFuture = completionService.submit(() -> {
Thread.sleep(1000L * (11 - finalI));
return Thread.currentThread().getName() + " : " + finalI;
});
futureList.add(stringFuture);
}
for (Future<String> stringFuture : futureList) {
String result = stringFuture.get();
System.out.println("阻塞时长:" + (System.currentTimeMillis() - currentTimeMillis1) + " result:" + result);
}
exec2.shutdown();
System.out.println("******************finished:" + (System.currentTimeMillis() - currentTimeMillis1));
}
执行结果:
2)使用 ExecutorCompletionService take的方式获取阻塞结果
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadFactory threadFactory1 = new ThreadFactoryBuilder().setNameFormat("batchCreate22222-%d").build();
ExecutorService exec2 = Executors.newFixedThreadPool(20, threadFactory1);
CompletionService<String> completionService = new ExecutorCompletionService<>(exec2);
long currentTimeMillis1 = System.currentTimeMillis();
for (int i = 1; i <= 10; i++) {
int finalI = i;
completionService.submit(() -> {
Thread.sleep(1000L * (11-finalI));
return Thread.currentThread().getName()+" : "+finalI;
});
}
for (int i = 1; i <= 10; i++) {
String result = completionService.take().get();
System.out.println("阻塞时长:" + (System.currentTimeMillis() - currentTimeMillis1) + " result:" + result);
}
exec2.shutdown();
System.out.println("******************finished:" + (System.currentTimeMillis() - currentTimeMillis1));
}
运行结果:
结果分析:
从运行结果看,获取阻塞结果时,并没有因为碰到执行最长的任务而一致阻塞,而是逐个取到了最先执行完成的任务;
原因:
ExecutorCompletionService 中添加了一个队列,来存放线程池已经完成的任务,take方法是从队列中获取已经完成的任务。