简体   繁体   English

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

[英]Is this singleton pattern thread safe?

I have a singleton server instance and I'm curious whether my code is thread safe. 我有一个单例服务器实例,我很好奇我的代码是否是线程安全的。 I've read about different singleton patterns, and I think the way to go generally is the double-checked locking pattern, which goes as follows: 我已经阅读了不同的单例模式,我认为通常的方法是double-checked locking模式,如下所示:

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

This is supposedly an efficient thread-safe way of setting/getting a singleton. 这被认为是设置/获取单身的有效线程安全方式。 The easiest way to get and set a singleton, I've read, is lazy instantiation , which looks like this: 我读过,获取和设置单例的最简单方法是lazy instantiation ,如下所示:

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

Now what I want to know is if my variation is thread-safe. 现在我想知道的是我的变体是否是线程安全的。 My code: 我的代码:

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

    server = new Server(listeningPortNumber);
}

My code is very similar to the lazy instantiation pattern above, but I can't see how my code isn't thread-safe. 我的代码与上面的惰性实例化模式非常相似,但我看不出我的代码是如何不是线程安全的。 Is there something I'm not seeing or is this actually valid code? 有没有我没看到的东西,或者这是真正有效的代码?


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

It's not safe. 这不安全。

Imagine what happens if two threads call startServer at the same time (or close enough to it): 想象一下如果两个线程同时调用startServer (或者足够接近它)会发生什么:

  1. Thread A checks server != null , and sees that server is null -- so it doesn't throw an exception 线程A检查server != null ,并看到该server为空 - 因此它不会抛出异常
  2. Thread B does the same 线程B也是如此
  3. Thread A now instantiates new Server(listeningPortNumber); 线程A现在实例化new Server(listeningPortNumber);
  4. Thread B does the same, and presumably bad things happen at this second instantiation 线程B做同样的事情,并且可能在第二次实例化时发生了不好的事情

If server isn't volatile , the problem is even worse, since you don't even need the interleaving anymore -- it's possible that Thread A instantiates the new Server(...) , but that the write to the server field isn't seen by Thread B for a long time (possibly forever) because it's not flushed to main memory. 如果server不是volatile ,问题就更糟了,因为你甚至不再需要交错 - 线程A可能实例化new Server(...) ,但写入server字段是'线程B看了很长时间(可能永远),因为它没有刷新到主存储器。

But the method is racy even if server is volatile , because of the interleaving. 但是,即使servervolatile ,由于交错,该方法也很有效。

No, lazy singleton pattern is not thread safe. 不,懒惰的单例模式不是线程安全的。

If you want a thread-safe version of the Singleton pattern in Java, you should implement Bill Pugh's solution. 如果你想在Java中使用线程安全版本的Singleton模式,你应该实现Bill Pugh的解决方案。 The code is here: 代码在这里:

public class OptimalSingleton
{
  protected OptimalSingleton()
  {
    // ...
  }

  private static class SingletonHolder
  {
    private final static OptimalSingleton INSTANCE = new OptimalSingleton();
  }

  public static OptimalSingleton getInstance()
  {
    return SingletonHolder.INSTANCE;
  }
}

More about it on SO: Singleton pattern (Bill Pugh's solution) 关于SO的更多信息: Singleton模式(Bill Pugh的解决方案)

Your code is not thread safe. 您的代码不是线程安全的。

Suppose you have two threads, A and B that call startServer(...) , and that prior to these calls the server has not been initialized. 假设您有两个线程,A和B,它们调用startServer(...) ,而在这些调用之前,服务器尚未初始化。 A passes one value (a) and B passes another (b) to the method. A传递一个值(a),B传递另一个值(b)到该方法。 The only thing you know is that within each thread the order of operations is defined. 您唯一知道的是,在每个线程中定义了操作的顺序。

So the following is possible: 所以以下是可能的:

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

Clearly this violates the singleton. 显然,这违反了单身人士。 Thread A will see a server that's initialized to a value different to the one passed in, and an exception was never thrown. 线程A将看到一个初始化为与传入的值不同的值的服务器,并且从未抛出异常。

Your code isn't thread safe! 您的代码不是线程安全的! Suppose 2 different threads simultaneously call startServer method Both see the server variable is null so will skip the if block And each thread creating an instance of Server! 假设2个不同的线程同时调用startServer方法两者都看到服务器变量为null所以将跳过if块并且每个线程创建一个Server实例! So you will end with 2 instance of a server in your application. 因此,您将在应用程序中以2个服务器实例结束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM