1. 痛点阐述
- 来源:因为使用ExecutorService返回的Future,使用Future去获取任务的返回值;会导致阻塞;因此,引入CompletionService来任务执行完成一个,则返回一个结果,不会因为其中的一个任务没结束,而导致获取结果的阻塞。
- 注意点:使用的时候,需要明白各个技术点的注意点是什么,CompletionService只是为了解决Future的get数据阻塞问题;而ExecutorService关注的则是任务的完成。
- 痛点:
- 场景1:当执行批量任务的时候,规定了多长时间去执行任务,若任务未执行完成,则返回未执行完的任务;但是,使用
executorService.shutdownNow()
;去打断在执行的任务;从而关闭线程池;但是,executorService.shutdownNow().
返回的List<Runnabole>
只是未执行的任务,并不包含被打断的任务;因此,造成了任务丢失了一个,且返回的任务,是无法获取到我们定义的任务的数据。
2. 解决方案.
- 重现问题:
private static void trap1() throws InterruptedException {
final ExecutorService executorService = Executors.newSingleThreadExecutor();
List<Runnable> runnableList = IntStream.range(0, 5).boxed()
.map(CompletionServiceDemo::task)
.collect(Collectors.toList());
ExecutorCompletionService<Object> completionService = new ExecutorCompletionService<>(executorService);
runnableList.stream().forEach(r -> completionService.submit(Executors.callable(r)));
TimeUnit.SECONDS.sleep(12);
List<Runnable> noExecTask = executorService.shutdownNow();
System.out.println("Task Size: " + noExecTask.size());
}
private static Runnable task(int i) {
return () -> {
try {
System.out.printf("Task[%d] is starting... \n", i);
TimeUnit.SECONDS.sleep(i * 5 + 10);
System.out.printf("Task[%d] is end... \n", i);
} catch (InterruptedException e) {
System.out.printf("Task[%d] is interrupt... \n", i);
}
};
}
- 解决问题
private static void killTrap1() throws InterruptedException {
final ExecutorService executorService = Executors.newSingleThreadExecutor();
List<Callable> tasks = IntStream.range(0, 5).boxed().map(MyTask::new).collect(Collectors.toList());
ExecutorCompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);
tasks.forEach(completionService::submit);
TimeUnit.SECONDS.sleep(12);
executorService.shutdownNow();
tasks.stream().filter(callable -> !((MyTask) callable).isSuccess()).forEach(System.out::println);
}
private static class MyTask implements Callable<Integer> {
private final Integer value;
private boolean success = false;
MyTask(Integer value) {
this.value = value;
}
@Override
public Integer call() throws Exception {
System.out.printf("Task[%d] is starting... \n", value);
TimeUnit.SECONDS.sleep(value * 5 + 10);
System.out.printf("Task[%d] is end... \n", value);
success = true;
return value;
}
public boolean isSuccess() {
return success;
}
@Override
public String toString() {
return "[ MyTask value = " + value + ", success = " + success + " ]";
}
}