CompletableFuture,可变对象和内存可见性
Posted
技术标签:
【中文标题】CompletableFuture,可变对象和内存可见性【英文标题】:CompletableFuture, mutable objects and memory visibility 【发布时间】:2016-03-29 10:24:25 【问题描述】:我试图了解 Java 8 中的 CompletableFuture
如何与 Java memory model 交互。在我看来,为了程序员的理智,理想情况下,以下内容应该是正确的:
-
线程中完成
CompletableFuture
的操作happen-before任何java.util.concurrent documentation 中有一条注释说:
在将
Runnable
提交给Executor
之前,线程中的操作happen-before 开始执行。对于Callable
s 提交到ExecutorService
也是如此。
这表明第一个属性为真,只要完成未来的线程执行 completion 依赖阶段或将其提交给Executor
。另一方面,在阅读CompletableFuture documentation 之后,我不太确定:
为非异步方法的依赖完成提供的操作可以由完成当前
CompletableFuture
的线程执行,或者由完成方法的任何其他调用者执行。
这让我想到了我的问题:
-
以上两个假设属性是真的还是假的?
在使用
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
添加到list2
对future
的依赖阶段可见?
【问题讨论】:
你能解释一下“注册完成的线程”是什么意思吗 @Misha: OP 显然意味着一个完成动作 或一个依赖阶段。 @Holger 如果他的意思是完成动作,那么这两个问题是相同的。他的意思是依赖阶段更合理。 @Misha:我的意思是“完成动作”,意思是“完成时要执行的动作”,即通过thenRun
提交。我承认这是模棱两可的,所以,既然所有的提交方法都会创建一个依赖阶段,那么这里应该首选术语“依赖阶段”。
@Misha @Holger 抱歉有任何歧义。 “完成”是指传递给thenRun
等的函数。“创建依赖阶段”和“执行依赖阶段”是这里的最佳术语吗?
【参考方案1】:
是的,你的两个假设都是正确的。原因是,CompletableFuture
中的所有*Async()
方法都将使用java.util.concurrent.Executor
进行异步调用。如果您不提供,这将是 common pool 或为每个任务创建 new thread 的 Executor(如果您将公共池的大小限制为 0 或 1)或用户提供的 Executor .
正如您已经发现的那样,Executor
的 documentation 表示:
在将 Runnable 对象提交给 Executor 之前,线程中的操作发生在其执行开始之前,可能在另一个线程中。
因此,在您的示例中,可以保证 "foo"
是您的 lambda 中 list1
的一部分,并且 list2
在后续阶段中可见。
Executor
的文档基本涵盖了这个。
【讨论】:
感谢您的回答!我正在努力理解的是,例如关于 2,如果完成未来的线程与创建依赖阶段的线程不同,那么在动作之间建立 happens-before 关系的创建依赖阶段的线程和依赖阶段的执行?Executor
保证的happens-before关系不就只保证完成future的线程中的action和依赖阶段的执行之间的关系吗?
@AapoLaitinen 两个线程之间的切换将始终通过Executor
。因此,根据定义,存在 happens-before 关系。完成 Future 具有 Future#get()
的语义,它也确实建立了 happens-before 关系。以上是关于CompletableFuture,可变对象和内存可见性的主要内容,如果未能解决你的问题,请参考以下文章