核心计数任务CompletableFuture比parallelStream慢

时间:2018-06-02 11:53:14

标签: java parallel-processing java-stream completable-future forkjoinpool

我的电脑是四核(FYI)

CompletableFuture将使用ForkJoinPool.commonPool()作为其official doc指出:

  

使用ForkJoinPool.commonPool()执行所有没有显式Executor参数的异步方法(除非它不支持至少两个的并行级别,在这种情况下,创建一个新的Thread来运行每个任务)。 / p>

我调试了CompletableFuture.supplyAsync(Supplier<U> supplier)

中的以下代码
private static final boolean useCommonPool =
    (ForkJoinPool.getCommonPoolParallelism() > 1);

/**
 * Default executor -- ForkJoinPool.commonPool() unless it cannot
 * support parallelism.
 */
private static final Executor asyncPool = useCommonPool ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

这意味着parallelStream始终使用ForkJoinPool.commonPool(),但这里为什么它更快。

我尝试将它们打印出来,并在使用CompletableFuture时发现只有三个线程:

private static int concurrencyGet() {
    List<CompletableFuture<Integer>> futureList = IntStream.rangeClosed(0, 10).boxed()
   .map(i -> CompletableFuture.supplyAsync(() -> getNumber(i)))
            .collect(Collectors.toList());
    return futureList.stream().map(future -> future.join()).reduce(0, Integer::sum);
}

但parallelStream使用,包括线程。

我的猜测是,在CompletableFuture.supplyAsync()中,ForkJoinPool.getCommonPoolParallelism(),而主要线程占据四个中的一个,因为它是异步

parallelStream 将耗尽所有,因为它不是异步

这是对的吗?我想知道这个问题是否有一些官方文件?

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

以下是我对 Venkat Subramaniam 关于Parallel and Asynchronous Programming with Streams and CompletableFuture的讨论的理解:

由于CompleteableFuture也使用ForkJoinPool.commonPool(),它也可以使用主线程,并且在某些情况下也可以。

给出以下示例

public static void main(String[] args) {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> numberSupplier());
    future.thenAccept(i -> System.out.println("f: " + i + " - " + Thread.currentThread()));
    sleep(100); //wait for async operations to finish before exiting
}

private static Integer numberSupplier() {
    Integer n = 2;
    System.out.println("c: " + n + " - " + Thread.currentThread());
    sleep(19);
    return n;
}

private static void sleep(int millis) {
    try {
        Thread.sleep(millis);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

你可能得到这样的控制台输出:

c: 2 - Thread[ForkJoinPool.commonPool-worker-1,5,main]
f: 2 - Thread[ForkJoinPool.commonPool-worker-1,5,main]

supplyAsync(..)以及thenAccept(..)部分都由ForkJoinPool的工作线程执行。

但是,如果给Supplier<Integer>的{​​{1}}如此之快,那么在调用supplyAsync(..)时它就完成了,那么第二部分也可能在主线程中执行:

thenAccept(..)

输出:

private static Integer numberSupplier() {
    Integer n = 2;
    //System.out.println("c: " + n + " - " + Thread.currentThread());
    //sleep(19);
    return n;
}