问题描述
我有一种预感,即使用持有者惯用语而不将持有者字段声明为final是不是线程安全的(由于Java中的不变性方式)。有人可以证实这一点(希望有一些消息来源)吗?
I have a hunch that using the holder idiom without declaring the holder field as final is not thread safe (due to the way immutability works in Java). Can somebody confirm this (hopefully with some sources)?
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;
}
}
编辑:我绝对需要源语句,不仅仅是它有效这样的断言 - 请解释/证明它是安全的
I definitely want sourced statements, not just assertions like "it works" -- please explain/prove it's safe
EDIT2:稍加修改以使我的观点更清楚 - 我能确定无论调用线程,getAnswer()方法都将返回21?
A little modification to make my point more clear - can I be sure that the getAnswer() method will return 21 regardless of calling thread?
推荐答案
保证如果使用静态初始化程序设置静态字段的值(即 static variable = someValue;
)该值对所有线程都可见:
The class initialization procedure guarantees that if a static field's value is set using a static initializer (i.e. static variable = someValue;
) that value is visible to all threads:
关于编辑,让我们想象一下两个线程T1和T2的情况,执行从挂钟的角度来看:
Regarding your edit, let's imagine a situation with two threads T1 and T2, executing in that order from a wall clock's perspective:
- T1:
Something s = Something.getInstance();
- T2:
Something s = Something.getInstance(); i = s.getAnswer();
- T1:
Something s = Something.getInstance();
- T2:
Something s = Something.getInstance(); i = s.getAnswer();
然后你有:
- T1获取LC,T1运行
Something INSTANCE = new Something();
,初始化回答
,T1释放LC - T2尝试获取LC,但已经被T1 =>等待锁定。当T1发布LC时,T2获取LC,读取
INSTANCE
然后读取answer
。
- T1 acquire LC, T1 run
Something INSTANCE = new Something();
, which initialisesanswer
, T1 release LC - T2 tries to acquire LC, but already locked by T1 => waits. When T1 releases LC, T2 acquire LC, reads
INSTANCE
then readsanswer
.
所以你可以看到你在写入和读取回答
之间有一个合适的事先关系,谢谢到 LC
锁定。
So you can see that you have a proper happens-before relationship between the write and the read to answer
, thanks to the LC
lock.
这篇关于初始化按需持有者成语线程安全,没有最终修饰符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!