初始化按需持有者成语线程安全,没有最终修饰符

我有一种预感,即使用持有者惯用语而不将持有者字段声明为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; } } 

编辑:我绝对想要源语句,而不仅仅是“它有效”的断言 – 请解释/certificate它是安全的

EDIT2:稍加修改以使我的观点更清楚 – 我可以确定getAnswer()方法将返回21而不管调用线程吗?

类初始化过程保证如果使用静态初始化程序(即static variable = someValue; )设置静态字段的值,则该值对所有线程可见:

10 – 如果初始化程序的执行正常完成,则获取LC,将C类的Class对象标记为完全初始化,通知所有等待的线程,释放LC ,并正常完成此过程。


关于你的编辑,让我们想象一下两个线程T1和T2的情况,从挂钟的角度按顺序执行:

  • T1: Something s = Something.getInstance();
  • T2: Something s = Something.getInstance(); i = s.getAnswer(); 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

因此,您可以看到,由于LC锁定,您在写入和读取之间存在正确的发生关系。

它确实是线程安全的,但是可变的。 因此,任何获得它的人都可以将其分配给其他人。 这是首先要担心的事情(甚至在考虑线程安全之前)。