【问题标题】:Is this singleton pattern thread safe?这个单例模式线程安全吗?
【发布时间】:2014-10-30 19:13:23
【问题描述】:

我有一个单例服务器实例,我很好奇我的代码是否是线程安全的。 I've read 关于不同的单例模式,我认为一般要走的路是double-checked locking 模式,如下:

public static Singleton getInstance() {
    if(singleton == null) {
        synchronized(Singleton.class) {
            if(singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

这应该是一种设置/获取单例的高效线程安全方式。我读过,获取和设置单例的最简单方法是lazy instantiation,如下所示:

public static ClassicSingleton getInstance() {
    if(instance == null) {
        instance = new ClassicSingleton();
     }
     return instance;
}

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

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

    server = new Server(listeningPortNumber);
}

我的代码与上面的 惰性实例化 模式非常相似,但我看不出我的代码不是线程安全的。有什么我看不到的东西还是这实际上是有效的代码?


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

【问题讨论】:

  • 您想做什么以确保线程安全?
  • @StackFlowed 这无关紧要
  • @StackFlowed 你为什么大喊
  • @Pshemo caps 被误按
  • @StackFlowed 我明白了。好吧,我想我真的不需要它,我只是好奇并想自学。

标签: java thread-safety singleton


【解决方案1】:

这不安全。

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

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

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

但是即使servervolatile,由于交错,该方法也很活泼。

【讨论】:

    【解决方案2】:

    不,惰性单例模式不是线程安全的。

    如果您想要 Java 中 Singleton 模式的线程安全版本,您应该实现 Bill Pugh 的解决方案。代码在这里:

    public class OptimalSingleton
    {
      protected OptimalSingleton()
      {
        // ...
      }
    
      private static class SingletonHolder
      {
        private final static OptimalSingleton INSTANCE = new OptimalSingleton();
      }
    
      public static OptimalSingleton getInstance()
      {
        return SingletonHolder.INSTANCE;
      }
    }
    

    更多关于它的信息:Singleton pattern (Bill Pugh's solution)

    【讨论】:

    • 我不是在问惰性实例化是否是线程安全的。我知道不是。我的变种呢?如果不是,你能解释一下原因吗?
    • 可能是的。但比比尔·普格的解决方案更昂贵的锁、双重检查等。那么你为什么要发明一种新的方法,它(可能)比现有的解决方案更糟糕。而且没有人知道,所以没有人能够轻松地理解您的代码。
    • 我对经过验证的模式不感兴趣。我只是好奇。
    • 我会说,是的,它是线程安全的。但是,如果getInstance()synchronized,您的解决方案背后的想法是相同的。除了你的解决方案会更慢,因为双 if.
    【解决方案3】:

    您的代码不是线程安全的。

    假设您有两个线程 A 和 B 调用 startServer(...),并且在这些调用之前服务器尚未初始化。 A 将一个值 (a) 传递给该方法,B 将另一个 (b) 传递给该方法。您唯一知道的是,在每个线程中定义了操作顺序。

    所以以下是可能的:

    A: check server != null  (false)
    B: check server != null  (false)
    A: server = new Server(a)
    B: server = new Server(b)
    

    显然这违反了单例。线程 A 将看到一个初始化为与传入的值不同的值的服务器,并且从未抛出异常。

    【讨论】:

      【解决方案4】:

      您的代码不是线程安全的! 假设 2 个不同的线程同时调用 startServer 方法 两者都看到服务器变量为空,因此将跳过 if 块 并且每个线程都创建一个 Server 实例!因此,您将以应用程序中的 2 个服务器实例结束。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-12
        • 1970-01-01
        • 2019-07-26
        • 1970-01-01
        • 1970-01-01
        • 2017-04-29
        相关资源
        最近更新 更多