JDK8中的CompletableFutures何时阻止执行线程?

时间:2017-06-28 09:26:19

标签: java multithreading java-8 completable-future

示例1:

CometableFuture
    .supplyAsync(new MySupplier())
    .someCompletableFutureMethod(new SomeCompletableFutureComsumer())

ForkJoin线程是否会被阻止?

示例2:

final CompletableFuture cf = new CompletableFuture();
cf = executor.execute(new Runnable(){
    public void run(){
        //do work
        cf.complete(result);
    }
});
cf.whenComplete(new MyConsumer());

所涉及的任何踏板是否都会受阻?

(我知道,我应该使用Callable而不是Runnable:)

有没有办法在不使用Future继承的方法来阻止任何线程(main,ForkJoin,executor)的情况下滥用API?

假设我没有使用任何阻止API(我知道future.get()阻止)。

3 个答案:

答案 0 :(得分:3)

请参阅get()的{​​{3}}:

  

如果有必要等待此未来完成,然后返回其结果。

换句话说:当你在CompletableFuture上调用“阻塞”方法时,它应该阻止。否则,它不会。

javadoc中没有方法的描述如下:可能会随机阻止 ;-)!

答案 1 :(得分:3)

CompletableFuture添加了join()方法,种类Future.get()docs here)的未经检查的例外版本。 我不建议使用它,因为通过不超时,它可以挂起线程,你几乎总是可以使用thenApply和朋友重写代码。 我尝试通过始终使用CompletionStage实现的CompletableFuture来强制执行此操作。

除了从Future块继承的方法之外,我不会想到任何其他方法。

答案 2 :(得分:1)

所有这些方法都需要某种形式的同步,如果没有阻塞就无法正确实现。

例如:

  1. supplyAsync()尝试通过其ForkJoin方法对公共execute()池中的作业进行排队。此方法依赖于UnsafeawaitRunStateLock()对作业进行排队;
  2. 类似的内容适用于您的executor.execute(),但您的实施可能会有所不同;
  3. 关于someCompletableFutureMethod()(和whenComplete()):
    1. 除了*Async()方法之外,它首先需要检查这个未来是否已经完成:如果是这种情况,传入的函数将在调用线程上执行,你可能认为它是阻塞的(虽然它实际上是在执行你的代码);
    2. 否则,任务必须排队,依赖于循环和Unsafe来执行此操作 - 请参阅CompletableFuture.*push*(*)方法。
  4. cf.complete()需要处理子任务,例如发送到whenComplete()的任务:
    1. 需要一些锁才能确保这些任务只执行一次;
    2. 它将立即执行所有非异步任务(如3.1中);
    3. 需要安排异步任务(如1.和2。)
  5. 接受lambda作为参数的大多数CompletableFuture方法(并返回一个新的CompletableFuture)实际上隐藏了一个complete()调用(在返回的将来),该调用将由同一个线程执行执行lambda,因此像4.¹
  6. 一样阻止它

    当然,除了在高度并发的环境中,许多线程试图同时推送和执行任务,你可能不会注意到这种阻塞。案例3.1和4.2是您最常遇到的案例(如果您使用非*Async()方法)。

    ¹thenCompose()方法有一个微妙的例外,因为执行complete()调用的线程将取决于lambda返回的CompletionStage是否已经完成。此外,非静态*Async()方法似乎重用了此调用的执行程序,因此可以使用另一个线程。