【问题标题】:CompletableFuture, mutable objects and memory visibilityCompletableFuture,可变对象和内存可见性
【发布时间】:2016-03-29 10:24:25
【问题描述】:

我试图了解 Java 8 中的 CompletableFuture 如何与 Java memory model 交互。在我看来,为了程序员的理智,理想情况下,以下内容应该是正确的:

  1. 线程中完成CompletableFuture的操作happen-before任何completions相关阶段都会被执行
  2. 线程中注册完成的操作创建了一个依赖阶段发生之前 completion依赖阶段被执行

java.util.concurrent documentation 中有一条注释说:

在将Runnable 提交给Executor 之前,线程中的操作happen-before 开始执行。对于Callables 提交到ExecutorService 也是如此。

这表明第一个属性为真,只要完成未来的线程执行 completion 依赖阶段或将其提交给Executor。另一方面,在阅读CompletableFuture documentation 之后,我不太确定:

为非异步方法的依赖完成提供的操作可以由完成当前CompletableFuture的线程执行,或者由完成方法的任何其他调用者执行。

这让我想到了我的问题:

  1. 以上两个假设属性是真的还是假的?
  2. 在使用CompletableFuture 时,是否有任何关于内存可见性保证的特定文档?

附录:

以具体示例的方式,考虑以下代码:

List<String> list1 = new ArrayList<>();
list1.add("foo");

CompletableFuture<List<String>> future =
        CompletableFuture.supplyAsync(() -> {
            List<String> list2 = new ArrayList<>();
            list2.addAll(list1);
            return list2;
        });

是否保证将 "foo" 添加到 list1 对 lambda 函数可见?是否保证将list1 添加到list2future 的依赖阶段可见?

【问题讨论】:

  • 你能解释一下“注册完成的线程”是什么意思吗
  • @Misha: OP 显然意味着一个完成动作 或一个依赖阶段。
  • @Holger 如果他的意思是完成动作,那么这两个问题是相同的。他的意思是依赖阶段更合理。
  • @Misha:我的意思是“完成动作”,意思是“完成时要执行的动作”,即通过thenRun提交。我承认这是模棱两可的,所以,既然所有的提交方法都会创建一个依赖阶段,那么这里应该首选术语“依赖阶段”。
  • @Misha @Holger 抱歉有任何歧义。 “完成”是指传递给thenRun 等的函数。“创建依赖阶段”和“执行依赖阶段”是这里的最佳术语吗?

标签: java java-8 java.util.concurrent java-memory-model completable-future


【解决方案1】:
  1. 是的,你的两个假设都是正确的。原因是,CompletableFuture 中的所有*Async() 方法都将使用java.util.concurrent.Executor 进行异步调用。如果您不提供,这将是 common pool 或为每个任务创建 new thread 的 Executor(如果您将公共池的大小限制为 0 或 1)或用户提供的 Executor . 正如您已经发现的那样,Executordocumentation 表示:

    在将 Runnable 对象提交给 Executor 之前,线程中的操作发生在其执行开始之前,可能在另一个线程中。

    因此,在您的示例中,可以保证 "foo" 是您的 lambda 中 list1 的一部分,并且 list2 在后续阶段中可见。

  2. Executor的文档基本涵盖了这个。

【讨论】:

  • 感谢您的回答!我正在努力理解的是,例如关于 2,如果完成未来的线程与创建依赖阶段的线程不同,那么在动作之间建立 happens-before 关系的创建依赖阶段的线程和依赖阶段的执行? Executor保证的happens-before关系不就只保证完成future的线程中的action和依赖阶段的执行之间的关系吗?
  • @AapoLaitinen 两个线程之间的切换将始终通过Executor。因此,根据定义,存在 happens-before 关系。完成 Future 具有 Future#get() 的语义,它也确实建立了 happens-before 关系。
猜你喜欢
  • 2012-03-11
  • 2021-02-27
  • 2016-12-23
  • 2011-10-12
  • 2017-03-18
  • 2011-02-23
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多