线程安全在Java中实现单例模式的有效方法?

时间:2010-12-19 10:30:42

标签: java singleton thread-safety synchronized

  

可能重复:
  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();
}
}

3 个答案:

答案 0 :(得分:21)

使用懒惰加载Singleton的最有效/最简单的方法就是

enum Singleton {
   INSTANCE
}

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

final class Singleton {
    private Singleton() { }
    static class SingletonHolder {
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
恕我直言,你必须非常偏执才能认为这是一个更好的解决方案。

答案 1 :(得分:3)

关于这个问题已经写了很多。是的,简单的双重检查锁定模式并不安全。但是你可以通过将静态实例声明为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()时才会加载嵌套类。

答案 2 :(得分:2)

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()时导致同步,从而影响性能。