Java Concurrency In Practice中的摘要/片段 -
// Unsafe publication
public Holder holder;
public void initialize(){
holder = new holder(42);
}
不正确发布的对象可能会出现两件事。其他 线程可以看到持有者字段的陈旧值,因此看到一个 null 引用或其他旧值,即使已有值 放在持有人。但更糟糕的是,其他线程可以看到更新 持有人参考的值,但是状态的陈旧值 支架即可。为了使事情更难以预测,一个线程可能会看到一个 第一次读取字段然后是更新时的陈旧值 下一次重视,这是为什么assertSanity可以抛出 的AssertionError
此外,对象引用对另一个线程可见,并不一定意味着该对象的状态对于使用线程是可见的
public class Holder{
private int n;
public Holder(int n) {
this.n = n;
}
public void assertSanity(){
if (n != n)
throw new AssertionError("This statement is false.");
}
}
Ofcouse,修复它的方法之一就是做/ make
public volatile Holder holder;
作者提出了一种不同的方法 -
如果持有人是不可变的,那么断言无法抛出 AssertionError,即使Holder未正确发布。)
public class Holder{
private final int n;
//...
}
但是怎么样?不安全的出版物仍在那里。我认为一个线程仍然可以获得持有者的 null 引用。请提出建议。
答案 0 :(得分:1)
jls描述了最终字段的特殊语义
https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5
final字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。线程安全的不可变对象被所有线程视为不可变的,即使使用数据争用传递线程之间的不可变对象的引用也是如此。 [...]。必须正确使用最终字段以提供不可变性的保证。
但我建议你阅读整章17.5
“必须正确使用”是指构造函数实际上已经结束(并且没有转义int()
)并且没有摆弄反射。
翻译自http://www.angelikalanger.com/Articles/EffectiveJava/38.JMM-Overview/38.JMM-Overview.html即:
构造函数的结尾导致部分刷新,将所有最终变量和dependend对象写入内存。 [...]。最终变量的第一次读取访问会导致部分刷新,从内存中加载最终变量和依赖对象。另一个没有发生[...]。