Java中CompletableFuture调用join()后是否保证字段更新的内存可见性?
Java中CompletableFuture调用join()后是否保证字段更新的内存可见性?
好问题!这其实涉及到Java内存模型(JMM)里的**先行发生(happen-before)**规则,而CompletableFuture的join()方法正好帮我们建立了关键的内存可见性保证,先给你一个明确结论:
在你提供的代码场景下,主线程在future.join()之后,一定会看到CompletableFuture任务中对x的y字段以及Y对象内部所有更新的内容,不需要额外使用volatile、AtomicInteger或者Collections.synchronizedList这类同步手段。
具体原因分析
Java并发包的规范明确规定:当CompletableFuture的异步任务完成时,任务内的所有操作与join()方法返回后的所有操作之间,存在一个**happen-before(先行发生)**关系。这意味着:
- 异步任务中执行的所有代码(包括创建
newY、设置value、添加名字、调用x.setY(newY)),都会先行发生于join()返回后主线程的任何操作。 - 根据Java内存模型的规则,先行发生的操作会把所有修改同步到主内存,后续线程(这里是主线程)读取这些值时,会从主内存获取最新的结果,而不是线程的本地缓存。
对应到你的代码场景
在你的Main类中:
- 异步任务线程完成了
newY的初始化、所有字段修改,以及x.setY(newY)的赋值操作。 future.join()会阻塞主线程,直到异步任务完全结束,此时JMM会把任务线程中所有修改的内存状态同步到主内存。- 主线程后续调用
x.getY().getValue()和getNames()时,会直接读取到主内存中的最新值,所以一定会输出42和[Alice, Bob]。
什么时候需要额外的同步?
如果你的场景是多个线程同时读写同一个共享对象(比如异步任务和主线程同时修改x的y字段,或者多个异步任务同时修改同一个Y对象),那才需要使用volatile、锁或者并发集合来保证线程安全和可见性。但在你当前的单写(异步任务)、读后写完成(主线程在join()后读)的场景下,完全不需要额外处理。
以下是你提供的完整代码,方便参考:
class Y { private int value; private List<String> names; public Y() { this.names = new ArrayList<>(); } public void setValue(int value) { this.value = value; } public int getValue() { return value; } public void addName(String name) { this.names.add(name); } public List<String> getNames() { return names; } } class X { private Y y; public void setY(Y y) { this.y = y; } public Y getY() { return y; } } public class Main { public static void main(String[] args) { X x = new X(); CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { Y newY = new Y(); newY.setValue(42); newY.addName("Alice"); newY.addName("Bob"); x.setY(newY); // Update the y field of x }); future.join(); // Wait for the CompletableFuture to complete // Will the main thread always see the updated y field? System.out.println("Value: " + x.getY().getValue()); // Should print 42 System.out.println("Names: " + x.getY().getNames()); // Should print [Alice, Bob] } }
备注:内容来源于stack exchange,提问作者Kaepxer
相关产品推荐
相关产品推荐

