这个单例模式线程安全吗?

Ola*_*oja 4 java singleton thread-safety

我有一个单例服务器实例,我很好奇我的代码是否是线程安全的.我已经阅读了不同的单例模式,我认为通常的方法是double-checked locking模式,如下所示:

public static Singleton getInstance() {
    if(singleton == null) {
        synchronized(Singleton.class) {
            if(singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}
Run Code Online (Sandbox Code Playgroud)

这被认为是设置/获取单身的有效线程安全方式.我读过,获取和设置单例的最简单方法是lazy instantiation,看起来像这样:

public static ClassicSingleton getInstance() {
    if(instance == null) {
        instance = new ClassicSingleton();
     }
     return instance;
}
Run Code Online (Sandbox Code Playgroud)

现在我想知道的是我的变体是否是线程安全的.我的代码:

public static void startServer(int listeningPortNumber) throws IOException {
    if (server != null) {
        throw new IOException("Connection exists");
    }

    server = new Server(listeningPortNumber);
}
Run Code Online (Sandbox Code Playgroud)

我的代码与上面的惰性实例化模式非常相似,但我看不出我的代码是如何不是线程安全的.有没有我没看到的东西,或者这是真正有效的代码?


参考:http://www.javaworld.com/article/2073352/core-java/simply-singleton.html

ysh*_*vit 7

这不安全.

想象一下如果两个线程同时调用startServer(或者足够接近它)会发生什么:

  1. 线程A检查server != null,并看到它server是null - 所以它不会抛出异常
  2. 线程B也是如此
  3. 线程A现在实例化 new Server(listeningPortNumber);
  4. 线程B做同样的事情,并且可能在第二次实例化时发生了不好的事情

如果server不是volatile,问题就更糟了,因为你甚至不再需要交错 - 线程A可能会实例化new Server(...),但是server线程B很长时间都看不到对字段的写入(可能永远)因为它没有冲到主存.

但是,即使servervolatile因为交错,这种方法也很有效.