执行以下代码时:
ExecutorService executorService = Executors.newWorkStealingPool(20);
Function<String, CompletableFuture<String>> requestTask =
url -> CompletableFuture.supplyAsync(() -> {
System.out.println("Request " + requestCount++ + " was sent");
HttpClient.get(url);
return url;
}, executorService);
Function<String, String> extractName = s -> s.replaceAll("(https|http|://|\\.com|www\\.|\\.io)", "");
CompletableFuture[] futures = urls.stream() // urls list contains 14 urls
.map(requestTask)
.map(future -> future.thenApply(extractName))
.map(future -> future.thenAccept(System.out::println))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures);
executorService.shutdown();
结果如下:
Request 0 was sent
Request 1 was sent
Request 2 was sent
Request 3 was sent
Request 4 was sent
Request 5 was sent
Process finished with exit code 0
但是,当Executors.newWorkStealingPool(20)
替换为Executors.newFixedThreadPool(20)
时,将发送所有请求。这种行为的原因是什么?
答案 0 :(得分:2)
并非发送所有请求,因为(剧透警报!)JVM只是终止了。
您可能知道,the conditions for JVM termination是:
发生以下两种情况之一:
- 已调用类
exit
的{{1}}方法,并且安全管理器已允许进行退出操作。- 不是守护程序线程的所有线程都已死,要么通过从调用返回到run方法,要么抛出传播到run方法之外的异常。
很明显,这不是第一种情况,因此必须是第二种情况。
首先要注意的是,您退出Runtime
方法:
main()
,但您对结果不做任何事情,因此不会阻塞(无CompletableFuture.allOf()
呼叫); join()
仅告诉执行程序关闭,它不会等待。最初,主线程是唯一的非守护程序线程,因此这对于JVM退出来说应该足够了。但这是2位执行者与众不同的地方:
ExecutorService.shutDown()
由newFixedThreadPool()
实现,该ThreadPoolExecutor
使用Executors.defaultThreadFactory()
,后者创建非守护线程; newWorkStealingPool()
由ForkJoinPool
实现,它在其创建的所有线程上调用setDaemon(true)
。不幸的是,它没有记载,但基本上,可以归结为Why does the following application terminate immediately when using ForkJoinPool, but not when I use ThreadPoolExecutor?
为您的问题提供两种可能的解决方案:
join()
之后致电allOf()
awaitTermination()
之后在执行器上调用shutdown()
¹As noted by teppic in the comments,在Java 8中没有记录,但是在it is now since Java 9中记录。