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

Thread Safe Efficient way to implement singleton pattern in Java?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Efficient way to implement singleton pattern in Java

我在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。静态字段的延迟初始化不正确。

最好的方法是什么?

这是正确的吗?

1
2
3
4
5
synchronized (Singleton.class) {
if (singleton== null) {
singleton= new Singleton();
}
}


最有效/最简单的方法就是

1
2
3
enum Singleton {
   INSTANCE
}

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

1
2
3
4
5
6
7
8
9
final class Singleton {
    private Singleton() { }
    static class SingletonHolder {
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

嗯,你必须非常偏执,才能认为这是一个更好的解决方案。


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

无论如何,在创建实例时,我很少真正需要这种懒散,所以我通常只是在类加载时静态地创建它:

1
private static MyClass instance = new MyClass();

这是简明扼要的。另一种选择是,如果您真的想让它变得懒惰,您可以利用类加载特性,并执行以下操作:

1
2
3
4
5
6
7
8
9
10
public class MyClass {
    private static class MyClassInit {
        public static final MyClass instance = new MyClass();
    }

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

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


在Java中实现单模式的有效方法的第一个代码示例是线程安全。INSTANCE的创建由类加载器在第一次加载类时执行;它只执行一次,并且以线程安全的方式执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
    }
}

(从Java中实现单模式的有效方法中复制了什么?)

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