使用双锁时使单例实例易变的重点是什么?

时间:2012-07-24 21:40:32

标签: java singleton volatile thread-synchronization double-checked-locking

private volatile static Singleton uniqueInstance

在单独使用双锁方法进行同步时,为什么单个实例声明为volatile?我可以在不将其声明为易失性的情况下实现相同的功能吗?

5 个答案:

答案 0 :(得分:49)

volatile可防止内存写入被重新排序,使其他线程无法通过单例指针读取单例的未初始化字段。

考虑这种情况:线程A发现uniqueInstance == null,锁定,确认它仍然是null,并调用singleton的构造函数。构造函数在Singleton中写入成员XYZ并返回。线程A现在将对新创建的单例的引用写入uniqueInstance,并准备释放其锁定。

正如线程A准备释放其锁定一样,线程B出现,并发现uniqueInstance不是null。线程B认为它已被初始化uniqueInstance.XYZ,但由于CPU已重新排序写入,因此线程A已写入XYZ的数据尚未对线程B可见。因此,线程B在XYZ内看到一个不正确的值,这是错误的。

当您标记uniqueInstance volatile时,会插入内存屏障。在uniqueInstance之前启动的所有写操作都将在修改uniqueInstance之前完成,从而防止上述重新排序情况。

答案 1 :(得分:24)

没有volatile,代码无法正常使用多个线程。

来自维基百科的Double-checked locking

  

从J2SE 5.0开始,此问题已得到修复。 volatile关键字现在确保多个线程正确处理单例实例。 The "Double-Checked Locking is Broken" Declaration

中描述了这个新的习语
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }

    // other functions and members...
}

一般情况下,如果可能的话,应该避免重复检查锁定,因为很难做到正确,如果你弄错了,很难找到错误。请尝试这种更简单的方法:

  

如果辅助对象是静态的(每个类加载器一个),则另一种选择是initialization on demand holder idiom

// Correct lazy initialization in Java 
@ThreadSafe
class Foo {
    private static class HelperHolder {
       public static Helper helper = new Helper();
    }

    public static Helper getHelper() {
        return HelperHolder.helper;
    }
}

答案 2 :(得分:11)

为避免使用双重锁定或易失性,请使用以下

enum Singleton {
     INSTANCE;
}

创建实例很简单,延迟加载和线程安全。

答案 3 :(得分:0)

在任何读取操作之前写入易失性字段。 以下是更好理解的示例代码:

private static volatile ResourceService resourceInstance;
//lazy Initialiaztion
public static ResourceService getInstance () {
    if (resourceInstance == null) { // first check
        synchronized(ResourceService.class) {
            if (resourceInstance == null) { // double check
                // creating instance of ResourceService for only one time
                resourceInstance = new ResourceService ();                    
            }
        }
    }
    return resourceInstance;
}

此链接可以更好地为您服务 http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html

答案 4 :(得分:-1)

您可以使用以下代码:

private static Singleton uniqueInstance;

public static synchronized Singleton getInstance(){
    if(uniqueInstance == null){
        uniqueInstance = new Singleton();
    }
    return uniqueInstance
}