与CompletableFuture一起使用时,WorkStealingPool和ThreadPoolExecutor产生不同的结果

时间:2018-08-25 17:57:17

标签: java concurrency executorservice completable-future

执行以下代码时:

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)时,将发送所有请求。这种行为的原因是什么?

1 个答案:

答案 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中记录。