41
private volatile static Singleton uniqueInstance

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

4

5 回答 5

66

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

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

就在线程 A 准备释放它的锁时,线程 B 出现了,发现它uniqueInstance不是null. 线程B访问uniqueInstance.XYZ时以为自己已经初始化了,但是由于 CPU 对写入进行了重新排序,线程 A 写入的数据XYZ并没有对线程 B 可见。因此,线程 B 在 内部看到了不正确的值XYZ,这是错误的。

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

于 2012-07-24T22:05:26.777 回答
32

如果没有volatile代码,多线程将无法正常工作。

来自维基百科的双重检查锁定

从 J2SE 5.0 开始,此问题已得到修复。volatile 关键字现在可确保多个线程正确处理单例实例。在“双重检查锁定被破坏”声明中描述了这个新的成语:

// 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...
}

一般来说,如果可能的话,你应该避免仔细检查锁定,因为它很难正确,如果你弄错了,也很难找到错误。试试这个更简单的方法:

如果辅助对象是静态的(每个类加载器一个),另一种方法是按需初始化持有者习惯用法

// 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;
    }
}
于 2012-07-24T21:44:07.633 回答
10

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

enum Singleton {
     INSTANCE;
}

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

于 2012-07-25T07:16:14.823 回答
1

写入 volatile 字段将在任何读取操作之前发生。 下面是一个示例代码,以便更好地理解:

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

于 2016-06-08T07:26:37.383 回答
-4

您可以使用以下代码:

private static Singleton uniqueInstance;

public static synchronized Singleton getInstance(){
    if(uniqueInstance == null){
        uniqueInstance = new Singleton();
    }
    return uniqueInstance
}
于 2017-05-17T02:53:05.043 回答