【问题标题】:Thread Safe Efficient way to implement singleton pattern in Java? [duplicate]线程安全在Java中实现单例模式的有效方法? [复制]
【发布时间】:2011-05-27 19:42:06
【问题描述】:

可能重复:
Efficient way to implement singleton pattern in Java

我正在阅读这个Best Singleton Implementation In Java,但它不是线程安全的。

根据维基:

if(singleton==null) { synchronized(Singleton.class) { // this is needed if two threads are waiting at the monitor at the // time when singleton was getting instantiated if(singleton==null) singleton= new Singleton(); }
}

但是 Find Bugs 实用程序在这方面给出了两个错误: 1.双重空检查。 2. 静态字段延迟初始化不正确。

什么是最好的方法,

是否正确:

synchronized (Singleton.class) {
if (singleton== null) {
singleton= new Singleton();
}
}

【问题讨论】:

标签: java singleton thread-safety synchronized


【解决方案1】:

制作延迟加载单例的最有效/最简单的方法就是

enum Singleton {
   INSTANCE
}

注意:不需要锁定,因为类加载是线程安全的。该类默认为final,不能通过反射调用构造函数。在使用实例或使用类之前,不会创建实例。如果您担心该类可能会被意外使用,您可以将单例包装在内部类中。

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

恕我直言,您必须非常偏执才能认为这是一个更好的解决方案。

【讨论】:

  • 在您的第二个代码中,您没有使用enum,但您使用的是class,所以我很困惑如何使用它。我想做一个枚举来初始化一个类,下次每次调用那个对象。我该怎么做。你能详细举个例子吗
  • @Manish 在这种情况下,使用enum,就像我在第一个示例中所做的那样。
  • 是的,没关系,但我怎样才能只初始化一个类并在下次使用enum 时使用该实例。那是我的困惑。我在哪里看到enum Singleton { INSTANCE } 这么多,但不知道如何调用和初始化对象
  • 您可以在代码中的任何位置使用Singleton.INSTANCE。以线程安全的方式调用一次的用于初始化此实例的代码将位于 enum 的构造函数中
  • @PeterLawrey 你没有忘记你的代码 sn-p 中的私有构造函数 Singleton() 吗?
【解决方案2】:

关于这个问题已经写了很多。是的,简单的双重检查锁定模式并不安全。但是您可以通过将静态实例声明为 volatile 来使其安全。新的 Java 内存模型规范在处理 volatile 时为编译器添加了一些代码重新排序限制,因此原来的风险已经消失。

反正我在创建实例时很少真正需要这种惰性,所以我通常只是在类加载时静态地创建它:

private static MyClass instance = new MyClass();

这是简短而清晰的。作为替代方案,如果你真的想让它变得懒惰,你可以利用类加载特性并这样做:

public class MyClass {
    private static class MyClassInit {
        public static final MyClass instance = new MyClass();
    }

    public static MyClass getInstance() {
        return MyClassInit.instance; 
    }
...
}

在您第一次调用 getInstance() 之前,不会加载嵌套类。

【讨论】:

    【解决方案3】:

    Efficient way to implement singleton pattern in Java 已接受答案中的第一个代码示例 线程安全的。 INSTANCE的创建由类加载器在第一次加载类时执行;它只执行一次,并且以线程安全的方式:

    public final class Foo {
    
        private static final Foo INSTANCE = new Foo();
    
        private Foo() {
            if (INSTANCE != null) {
                    throw new IllegalStateException("Already instantiated");
            }
        }
    
        public static Foo getInstance() {
            return INSTANCE;
        }
    }
    

    (复制自What is an efficient way to implement a singleton pattern in Java?

    问题中的第二个代码示例是正确且线程安全的,但它会导致每次调用 getInstance() 时同步,从而影响性能。

    【讨论】:

    • 不保护私有构造函数是相当偏执的。我假设它避免使用反射创建另一个实例,还是停止内部类调用构造函数?
    • 我正在讨论上一个问题的答案,所以我按原样复制了代码。我个人也会省略if (INSTANCE != null) 检查。
    • 因此我认为是例外。 ;)
    • 您好 Eli,从您的回答中我了解到 Foo 对象将只有一个副本。但是假设两个线程同时调用 getInstance() 函数。那么我相信会有一个比赛条件。如何避免这种情况。因此我们需要避免这种情况。请查看 [link]stackoverflow.com/questions/15930633/… 以获得最终答案...
    猜你喜欢
    • 2014-10-07
    • 1970-01-01
    • 1970-01-01
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多