来自 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
会发生什么引用?阅读安全吗?最佳答案
因为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
初始化,不管它是不是volatile
或 final
或者只是一个普通的领域。由于在字段写入和线程开始之前存在 Happens-Before 关系,即使您在构造函数中创建的线程也会看到 map
如初始化。请注意,我特别写了“在执行构造函数中的代码之前”而不是“在执行构造函数之前”,因为这不是 JSL 12.5 做出的保证(阅读它!)。这就是为什么您在构造函数代码的第一行之前在调试器中看到 null 的原因,但保证构造函数中的代码看到该字段已初始化。
关于java - 使用 volatile 字段安全发布对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67956977/