【问题标题】:Which executor does CompletableFuture.allOf use?CompletableFuture.allOf 使用哪个执行器?
【发布时间】:2018-10-02 05:54:44
【问题描述】:

假设我们有两个执行者,1 和 2。

我们可以在做的时候配置使用哪个executor

CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(()-> {return 1;}, executor1) //executor1
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(()-> {return 2;}, executor1) //executor1
CompletableFuture<Integer> cf3 = CompletableFuture.supplyAsync(()-> {return 3;}, executor2) //executor2

但是哪个线程执行器使用了 CompletableFuture 静态方法 allOf?

CompletableFuture.allOf(cf1, cf2, cf3)

谢谢!

【问题讨论】:

  • 有默认执行器Executor asyncPool = useCommonPool ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
  • @Andrew 默认执行器在这里不涉及,因为没有要执行的任务。

标签: java parallel-processing java-8 completable-future


【解决方案1】:

The answer of Ivan Gammel 不准确。

确实没有与allOf() 返回的CompletableFuture 关联的执行程序,因为事实上,从来没有与任何CompletableFuture 关联的执行程序。

任务与执行器相关联,因为它在执行器内部运行,但关联是相反的:执行器有一个要执行的任务列表。

任务也可以与CompletableFuture 相关联,它会在任务完成时完成。 CompletableFuture 本身不保留对用于创建它的任务或执行程序的引用。然而,它可能会保留对任务的引用以及在相关阶段中使用的可选执行器。

allOf()返回的CompletableFuture会被一个task完成,这个task是原来CompletableFutures的一个依赖阶段。在您的示例中,此任务可以通过以下方式执行:

  • executor1,如果第三个任务先完成;
  • executor2,如果前两个任务在第三个任务之前完成;或
  • 原始线程,如果所有任务在您调用 allOf() 之前完成。

这可以通过在allOf() 调用中添加一个依赖的thenRun() 阶段来看到:

public class CompletableFutureAllOfCompletion {
    private ExecutorService executor1 = Executors.newFixedThreadPool(2);
    private ExecutorService executor2 = Executors.newFixedThreadPool(2);
    private Random random = new Random();

    public static void main(String[] args) {
        new CompletableFutureAllOfCompletion().run();
    }

    public void run() {
        CompletableFuture<Integer> cf1 = supplyAsync(this::randomSleepAndReturn, executor1);
        CompletableFuture<Integer> cf2 = supplyAsync(this::randomSleepAndReturn, executor1);
        CompletableFuture<Integer> cf3 = supplyAsync(this::randomSleepAndReturn, executor2);
        randomSleepAndReturn();
        CompletableFuture.allOf(cf1, cf2, cf3)
                .thenRun(() -> System.out.println("allOf() commpleted on "
                        + Thread.currentThread().getName()));

        executor1.shutdown();
        executor2.shutdown();
    }

    public int randomSleepAndReturn() {
        try {
            final long millis = random.nextInt(1000);
            System.out.println(
                    Thread.currentThread().getName() + " waiting for " + millis);
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 0;
    }
}

一些可能的输出:

在第一个执行器上完成(第三个任务先完成):

pool-1-thread-1 waiting for 937
pool-1-thread-2 waiting for 631
main waiting for 776
pool-2-thread-1 waiting for 615
allOf() commpleted on pool-1-thread-1

在第二个执行器上完成(第一个和第二个任务在第三个之前完成):

pool-1-thread-1 waiting for 308
pool-1-thread-2 waiting for 788
main waiting for 389
pool-2-thread-1 waiting for 863
allOf() commpleted on pool-2-thread-1

在主线程上完成(所有任务在allOf().thenRun()之前完成):

pool-1-thread-1 waiting for 168
pool-1-thread-2 waiting for 292
main waiting for 941
pool-2-thread-1 waiting for 188
allOf() commpleted on main

如何控制allOf()(或anyOf())之后使用的执行器

由于无法保证将使用的执行器,因此调用其中一种方法后应调用*Async(, executor) 来控制将使用哪个执行器。

如果您需要返回其中一个调用的结果 CompletableFuture,只需在返回之前添加 thenApplyAsync(i -&gt; i, executor)

【讨论】:

  • @Anatoly 仅供参考,您的编辑并没有改变代码突出显示的任何内容(因为问题已经有 Java 标记,所以会自动应用突出显示),但是它使代码缩进了 4 个空格。因此,我恢复了该更改。
【解决方案2】:

没有与CompletableFuture#allOf 关联的执行程序,它只生成CompletableFuture,它将在您将调用CompletableFuture#get() 的同一线程中等待依赖项完成。

在你的例子中,cf1cf2后面的任务仍将由executor1执行,cf2中的任务将由executor2执行,allOf(..).get()的结果将在当前线程,不会在后台启动其他线程。

这是一个示例,您可以通过在 System.out.println 行上设置断点并检查活动线程列表来观察 IDE 中的实际行为。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Supplier;

import static java.util.concurrent.CompletableFuture.supplyAsync;

public class ExecutorTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Executor executor1 = Executors.newSingleThreadExecutor();
        Executor executor2 = Executors.newSingleThreadExecutor();
        CompletableFuture<Integer> cf1 = supplyAsync(run(1), executor1); //executor1
        CompletableFuture<Integer> cf2 = supplyAsync(run(2), executor1); //executor1
        CompletableFuture<Integer> cf3 = supplyAsync(run(3), executor2); //executor2
        CompletableFuture<Void> result = CompletableFuture.allOf(cf1, cf2, cf3);
        new Thread(() -> {
            try {
                result.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("Waiting now...");
    }

    private static Supplier<Integer> run(int result) {
        return () -> runDelayed(result);
    }

    private static int runDelayed(int result) {
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

}

【讨论】:

    猜你喜欢
    • 2021-02-24
    • 1970-01-01
    • 2016-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 2015-02-13
    • 1970-01-01
    相关资源
    最近更新 更多