【问题标题】:Are there any issues in multithreaded environment with Singleton pattern?单例模式在多线程环境中是否存在任何问题?
【发布时间】:2011-11-30 06:58:22
【问题描述】:

我已经实现了如下的 Singleton 类:

public class Singleton {

    private static  Singleton instance = null;


    private Singleton() { 
    }

private synchronized static void createInstance() {
    instance = new Singletone();
}


    public static Singleton getInstance() {
        if(instance == null){
            createInstance();
        }
        return instance;
    }

}

但我想知道它是否是单例的正确实现。 多线程环境有没有问题。

【问题讨论】:

标签: java multithreading design-patterns singleton


【解决方案1】:

您的实现几乎是正确的。问题是它不是线程安全的。 2 个单独的线程可能同时进入getInstance(),检查实例是否为空,然后创建您的类的 2 个实例。这是修复:

public static synchronized Singletone getInstance() {
    if(instance == null){
        createInstance();
    }
    return instance;
} 

请注意字词synchronized

【讨论】:

    【解决方案2】:
    public enum Singleton {
        INSTANCE;
        private int val;
    
        public int getVal() {
            return val;
        }
    }
    

    用法:

    Singleton.INSTANCE.getVal();
    

    对于具有枚举支持的 Java 版本 > 5.0,这是完美的单例。

    在 Joshua Bloch 的 Effective Java 中也提到过。这里有一篇关于它的博客文章:Enum Singleton

    更新:
    另外,请仅在您 100% 确定需要单例时才使用!它会破坏代码的可测试性!但是你不能在某些地方避免它,比如在工厂里。
    但请不要滥用它,在您真正需要的地方使用它。了解它的用途。

    【讨论】:

    • 抱歉,不小心投了反对票,结果我不清楚,所以这个 +1 是下一个最好的事情。
    【解决方案3】:

    除了上面的枚举之外,我遇到的最好的机制称为静态初始化。有了这个,您就可以依赖 Java 内存模型的保证,因此可以保证它始终有效。这是来自an answer to a different question 的一个 sn-p,它证明了这一点:

    class Singleton {
       static class SingletonHolder {
          static final Singleton INSTANCE = new Singleton();
       }
       public static Singleton instance() {
          return SingletonHolder.INSTANCE;
       }
    }
    

    第一次调用SingletonHolder.INSTANCE 时,将创建具有Singleton 实例化的SingletonHolder 类对象。

    Java 内存模型保证静态代码 (new Singleton()) 只会由一个线程执行。所以没有双重检查锁定(不起作用),也没有不必要的同步。所有后续调用都将获取该实例。

    【讨论】:

      【解决方案4】:

      这是单例模式的正确实现,虽然你并不需要createInstance 方法;你可以把它内联到getInstance。另外,拼写为“Singleton”,末尾没有“e”。

      假设您可以在多线程环境中创建问题。如果两个帧指针同时进入getInstance,第一个进入的会得到Singleton的实例,第二个会得到另一个实例。

      这取决于你如何使用它。如果在设置线程之前使用单例,则不会有问题。如果这是一个问题,您可以考虑先初始​​化单例。您也可以通过在方法声明中使用synchronized 关键字来解决此问题。

      【讨论】:

        【解决方案5】:

        对于延迟初始化实现,即您的情况,getInstance() 方法应同步以确保线程安全。

        public static synchronized Singleton getInstance()
        

        或者您可以在已经是线程安全的类加载时简单地对其进行初始化。

        private static  Singleton instance = new MySingleton(); 
        

        【讨论】:

          【解决方案6】:

          单例不是线程安全的。正是因为这个原因,才引入了Double-checked Locking

          从 Java SE 5 及更高版本开始,您可以volatile 您的静态实例。当多个线程运行时,Java VM 将知道如何正确处理 Singleton。

          更多关于Double-checked locking

          【讨论】:

          • 来自您包含的链接:“这种技术有许多微妙的问题,通常应该避免”
          【解决方案7】:

          您的实现似乎还不错。单个 JVM 中的多线程对于那种单例问题来说不是问题,但它会导致集群(两个或多个 JVM)出现问题。

          有关这些问题的详细信息,请参阅 http://java.sun.com/developer/technicalArticles/Programming/singletons/

          哦,它是单例的,而不是单例的。 :)

          【讨论】:

          • 实际上它一个可能*多线程问题,即使在单个 JVM 中也是如此。您的链接还在“不正确同步导致的多个实例”下对此进行了介绍。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-09-15
          • 2020-02-08
          • 1970-01-01
          • 2014-01-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多