Java 设置此引用线程安全吗?

Java 设置此引用线程安全吗?,java,multithreading,Java,Multithreading,对于这段代码是否是线程安全的,我一直得到各种各样的答案。我在Java8中工作 private final Object lock = new Object(); private volatile Object reference = null; public Object getOrCompute(Supplier<Object> supplier) { if (reference == null) { synchronised(lock) {

对于这段代码是否是线程安全的,我一直得到各种各样的答案。我在Java8中工作

private final Object lock = new Object();
private volatile Object reference = null;

public Object getOrCompute(Supplier<Object> supplier) {
    if (reference == null) {
        synchronised(lock) {
            if (reference == null) {
                reference = supplier.get();
            }
        }
    }

    return reference;
}
private final Object lock=new Object();
私有易失性对象引用=null;
公共对象getOrCompute(供应商){
if(reference==null){
同步(锁定){
if(reference==null){
reference=supplier.get();
}
}
}
返回参考;
}

我的期望是,给定这个类的一个新实例,对
getOrCompute()
的多次调用只会导致调用一个供应商,而该供应商的结果是对
getOrCompute()
不得在分配到
参考时重新排序。(或者更准确地说,当您对
参考
进行易变读取时,它不能看起来被重新排序)

锁提供排他性,易变写/读语义提供可见性。请注意,这是自很久以前发布的Java5以来才出现的情况,但是您仍然可以在Internet上找到关于双重检查锁定(因为这是这个习惯用法的官方名称)如何不起作用的过时文章。他们当时是对的,但现在已经过时了


但是,如果供应商提供一个可变对象,那么它本身可能是不安全的。但这是另一回事。

同步不是线程安全的。它会阻止线程一次访问对象,但它无法控制哪个线程何时获得对象,或者一旦获得对象的访问权,它将如何处理对象。同步一次只限制对一个线程的访问,首先访问它的线程首先访问它

在这种情况下,它所做的唯一一件事就是阻止多个线程实例化对象。如果对象已经实例化,它将被分发给任何线程想要的对象,而没有线程安全性


假设您有一个线程访问该方法并实例化该对象,它检索该对象,而当它检索该对象时,另一个线程正试图实例化它,这是不允许的,因为它存在,所以它可以直接跳到检索对象,就像线程1一样,因此,它们现在可以同时修改对象,而不是线程安全的。但是新对象的实例化是线程安全的,因为对象只能实例化一次。

根据一些相当重的Java名称,由于
volatile
引用,您的代码是线程安全的。我希望
supplier.get()
永远不能返回
null
@AndyBrown如果返回会有什么问题null@grape_mao每次调用
getOrCompute()时都会调用它
。很高兴听到您所听到的关于此代码为何不是线程安全的论点。引用的可变性与赋值的线程安全无关。@Vogel612我并没有暗示它是线程安全的(请参阅我答案的前三个字:))。这是一个旁白。或者至少我不是有意暗示的。我可能是无意中这样做的:)@Gemtastic变量是私有的,可以安全地假设OP在帖子中包含了所有相关的代码。@Gemtastic问题是这段代码是否安全。如果将来添加了一些假设性的代码,它是否会保持安全并不重要。当然,如果您多次设置
reference
的值,它将不会保持安全,但这将是一个不同的代码。
在这种情况下,它所做的唯一事情是阻止多个线程实例化对象。
它做的远不止这些,它确保了所有后续调用方都能看到所提供的对象。这就是“安全”的定义。是的,我把它在同步块中的作用称为“实例化”,因为当你把它推到边缘时,它就是这样做的;它将
reference
的值/引用替换为
supplier.get()
中的任何值/引用,以便零件是螺纹安全的;只有一个线程可以做到这一点。但是
reference
对象不是线程安全的,可以根据接收它的线程的意愿进行更改。OP的示例演示了一种众所周知的设计模式,即双重检查锁定。双重检查锁定的全部要点是以线程安全的方式初始化单例对象。因此,如果它所做的唯一一件事是阻止多个线程实例化和使用类的不同实例,那么它将完全按照它应该做的做,并且以线程安全的方式来做。