设置此参考线程是否安全?

时间:2015-07-06 11:42:44

标签: java multithreading

我不断得到关于此代码是否是线程安全的混合答案。我在Java 8工作。

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;
}

我的期望是,给定此类的新实例,多次调用getOrCompute()只会导致一个供应商被调用,而该供应商的结果是所有调用(以及将来的调用)的结果getOrCompute()

2 个答案:

答案 0 :(得分:3)

这是安全的,因为supplier.get()中所做的任何事情都不能与reference的作业重新排序。 (或者更确切地说,当您对reference进行易失性读取时,它似乎不会被重新排序。)

锁提供排他性,易失性写/读语义提供可见性。请注意,自从很久以前发布的Java 5以来,这一点才真实,但您仍然会在互联网上找到关于双重检查锁定方式的过时文章(因为这是官方名称)这个成语)不起作用。他们当时是对的,但现在已经过时了。

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

答案 1 :(得分:0)

同步不是线程安全的。它阻碍了线程一次性访问对象,但它无法控制哪个线程在对象获得访问权限时对其进行操作的时间或内容。同步仅限制当时对一个线程的访问,首先访问它的线程首先访问它。

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

想象一下,你有一个线程访问该方法并实例化该对象,它检索它并在它检索对象时,另一个线程试图实例化它,它不会被允许,因为它因为它可以直接跳转到检索对象,就像第一个线程一样,这些现在可以同时修改对象,而不是线程安全。但是新对象的实例化是线程安全的,对象只能实例化一次。