gpt4 book ai didi

java - 使用 volatile 字段安全发布对象

转载 作者:行者123 更新时间:2023-12-04 00:49:03 26 4
gpt4 key购买 nike

来自 Java Concurrency In Practice 一书:

To publish an object safely, both the reference to the object and the object’s state must be made visible to other threads at the same time. A properly constructed object can be safely published by:

  • Initializing an object reference from a static initializer;
  • Storing a reference to it into a volatile field or AtomicReference;
  • Storing a reference to it into a final field of a properly constructed object; or
  • Storing a reference to it into a field that is properly guarded by a lock.

我的问题是:
为什么要点 3 具有约束:“ 的正确构造的对象 ”,而要点 2 没有?
以下代码是否安全地发布 map实例?我认为代码符合要点 2 的条件。
public class SafePublish {

volatile DummyMap map = new DummyMap();

SafePublish() throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
// Safe to use 'map'?
System.out.println(SafePublish.this.map);
}
}).start();
Thread.sleep(5000);
}

public static void main(String[] args) throws InterruptedException {
SafePublish safePublishInstance = new SafePublish();
}


public class DummyMap {
DummyMap() {
System.out.println("DummyClass constructing");
}
}
}
以下调试快照图片显示了 map实例是 null执行时正在进入 SafePublish的施工中.如果另一个线程现在试图读取 map 会发生什么引用?阅读安全吗?
enter image description here

最佳答案

因为final字段保证对其他线程可见 只有在对象构建之后 而写入 volatile 的可见性字段保证没有任何附加条件。
来自 jls-17 , 在 final领域:

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.


volatile领域:

A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).


现在,关于您的具体代码示例, JLS 12.5保证在执行构造函数中的代码之前进行字段初始化(请参阅 JLS 12.5 中的步骤 4 和 5,此处引用有点太长)。因此,程序顺序保证构造函数的代码会看到 map初始化,不管它是不是 volatilefinal或者只是一个普通的领域。由于在字段写入和线程开始之前存在 Happens-Before 关系,即使您在构造函数中创建的线程也会看到 map如初始化。
请注意,我特别写了“在执行构造函数中的代码之前”而不是“在执行构造函数之前”,因为这不是 JSL 12.5 做出的保证(阅读它!)。这就是为什么您在构造函数代码的第一行之前在调试器中看到 null 的原因,但保证构造函数中的代码看到该字段已初始化。

关于java - 使用 volatile 字段安全发布对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67956977/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com