【问题标题】:When do CompletableFutures in JDK8 block the execution threads?JDK 8 中的 CompletableFuture 何时阻塞执行线程?
【发布时间】:2017-06-28 09:26:18
【问题描述】:

示例 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 继承的方法的情况下滥用 API 来阻塞任何线程(main、ForkJoin、executor)?

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

【问题讨论】:

  • 当您执行阻塞操作时它们会阻塞。你在问什么?
  • 假设我没有使用任何阻塞 API 并且我没有调用 Thread.sleep。这是工作人员之间的辩论:)))
  • 您在争论线程是否会无缘无故地自发阻塞?你能提供一些来自“是的”团队的论据吗?
  • 拥护者有论据。到目前为止,您还没有提出任何内容。
  • 或者辩论真的是“他们不能被阻止”与“好吧,他们可能会”?

标签: java multithreading java-8 completable-future


【解决方案1】:

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

除了从Future 块继承的方法之外,我认为没有其他方法。

【讨论】:

    【解决方案2】:

    查看javadoc 获取get()

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

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

    该 javadoc 中的任何方法都没有这样的描述:可能会随机阻塞 ;-) !

    【讨论】:

      【解决方案3】:

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

      例如:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-16
        相关资源
        最近更新 更多