我有预感,使用holder 习惯用法而不将holder 字段声明为final 不是线程安全的(由于Java 中不变性的工作方式)。有人可以证实这一点吗(希望有一些消息来源)?
public class Something {
private long answer = 1;
private Something() {
answer += 10;
answer += 10;
}
public int getAnswer() {
return answer;
}
private static class LazyHolder {
// notice no final
private static Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
编辑:我绝对想要来源声明,而不仅仅是像“它有效”这样的断言——请解释/证明它是安全的
EDIT2:稍作修改以使我的观点更清楚 - 我可以确定 getAnswer() 方法将返回 21 无论调用线程如何?
The 类初始化过程保证如果使用静态初始值设定项设置静态字段的值(即static variable = someValue;
)该值对所有线程都可见:
10 - 如果初始化程序的执行正常完成,则获取 LC,将 C 的 Class 对象标记为完全初始化,通知所有等待线程,释放 LC,并正常完成此过程。
关于您的编辑,让我们想象一下有两个线程 T1 和 T2 的情况,从挂钟的角度按该顺序执行:
- T1:
Something s = Something.getInstance();
- T2:
Something s = Something.getInstance(); i = s.getAnswer();
那么你有:
- T1 获取 LC,T1 运行
Something INSTANCE = new Something();
,它初始化answer
, T1释放LC
- T2 尝试获取 LC,但已被 T1 锁定 => 等待。当T1释放LC时,T2获取LC,读取
INSTANCE
然后读answer
.
所以你可以看到你在写入和读取之间有一个正确的happens-before关系answer
, 感谢LC
lock.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)