Java 按需初始化持有者习惯用法在没有最终修改器的情况下是线程安全的吗

Java 按需初始化持有者习惯用法在没有最终修改器的情况下是线程安全的吗,java,multithreading,singleton,immutability,idioms,Java,Multithreading,Singleton,Immutability,Idioms,我有一种预感,在不将holder字段声明为final的情况下使用holder习惯用法不是线程安全的(这是由于Java中不变性的工作方式)。有人能证实这一点吗(希望有一些消息来源) 编辑:我绝对想要源语句,而不仅仅是“它有效”这样的断言——请解释/证明它是安全的 EDIT2:为了让我的观点更清晰,我做了一点修改-我可以确保getAnswer()方法将返回21而不管调用线程吗?它肯定是线程安全的,但是是可变的。因此,任何获得它的人都可以将其分配给其他对象。这是首先要担心的(甚至在考虑线程安全性之前)

我有一种预感,在不将holder字段声明为final的情况下使用holder习惯用法不是线程安全的(这是由于Java中不变性的工作方式)。有人能证实这一点吗(希望有一些消息来源)

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


EDIT2:为了让我的观点更清晰,我做了一点修改-我可以确保getAnswer()方法将返回21而不管调用线程吗?

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

保证如果使用静态初始值设定项设置静态字段的值(即
静态变量=someValue;
),则该值对所有线程可见:

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


关于您的编辑,让我们设想一种情况,有两个线程T1和T2,从挂钟的角度来看,它们按顺序执行:

  • T1:
    Something s=Something.getInstance()
  • T2:
    Something s=Something.getInstance();i=s.getAnswer()
那么你有:

  • T1获取LC,T1运行
    Something实例=newsomething(),它初始化
    应答
    ,T1释放LC
  • T2尝试获取LC,但已被T1=>等待锁定。当T1释放LC时,T2获取LC,读取
    实例
    ,然后读取
    应答

因此您可以看到,由于
LC
锁,
Something
类之外的任何人都不能从我这里得到它。是的,这是真的+1。所以实际上,易变性问题只能发生在Something类的方法中。但我认为它是线程安全的,因为类加载无论如何都是同步的(不管是final还是not final)。@MarkoTopolnik-当然任何人都可以使用public方法获取实例,你是什么意思?@JakubBochenski我的意思是分配它。@JakubBochenski你也可以使用反射修改
私有静态final
字段(除非它是编译时常量)。它是完全线程安全的,最终的还是非最终的。此断言的来源?Java内存模型-我将回答…哎呀,太晚了:@assylias已经为我做了。@edit-回答后我很清楚这一点,但谢谢你的示例anyway@assylias,我不确定当
s
(参考)已发布,但
答案
尚未初始化,或者我错了?谢谢。
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;
    }

}