我正在读这个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(); } }
答案 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()
时导致同步,从而影响性能。