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

线程安全在Java中实现单例模式的有效方法?,java,singleton,thread-safety,synchronized,Java,Singleton,Thread Safety,Synchronized,可能重复: 我正在读这篇文章,但它不是线程安全的 根据维基: if(singleton==null){ 已同步(Singleton.class){// 如果需要两个线程,则需要执行此操作 此时正在监视器旁等待 当辛格尔顿 实例化的if(singleton==null) singleton=新singleton();} } 但是查找bug实用程序在这方面给出了两个错误: 1.双重空检查。 2.静态字段的延迟初始化不正确 最好的办法是什么, 是否正确: 创建延迟加载单例的最有效/最简单的方法是

可能重复:

我正在读这篇文章,但它不是线程安全的

根据维基:

if(singleton==null){
已同步(Singleton.class){//
如果需要两个线程,则需要执行此操作
此时正在监视器旁等待
当辛格尔顿
实例化的if(singleton==null)
singleton=新singleton();}
}

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

最好的办法是什么,

是否正确:


创建延迟加载单例的最有效/最简单的方法是

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;
    }
}

IMPO,你必须非常偏执地考虑这是一个更好的解决方案。

< P>在接受的答案中的第一个代码示例是线程安全的。
实例的创建在类第一次加载时由类加载器执行;它以线程安全的方式执行一次:

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;
    }
}
(抄自)


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

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

没有保护私有构造函数是相当偏执的。我假设这是为了避免使用反射创建另一个实例,还是为了停止内部类调用构造函数?我在讨论前一个问题的答案,所以我照样复制了代码。就我个人而言,如果(INSTANCE!=null)
检查,我也会省略
,因此我假设是例外情况嗨,伊莱,根据你的回答,我知道会有一个Foo对象的副本。但是假设两个线程同时调用getInstance()函数。然后我相信会有一个比赛条件。如何避免,因此我们需要避免。请查看[链接]以获得最终答案…这是上述问题的副本;详见该问题。但我不确定这个问题是否有这些有用的链接,所以:哪些链接指向Java5的更新。另见。但是对于你问题的实际答案,请参见上面链接的问题。在你的第二个代码中,你没有使用
enum
,但是你使用的是
class
,因此我不知道该如何理解。我想做一个枚举来初始化一个类,下次再调用那个对象。我怎么做呢?你能举个例子吗detail@Manish在这种情况下,使用
enum
,就像我在第一个示例中所做的那样。是的,这是可以的,但是我如何使用
enum
只初始化一次类并在下次使用该实例呢。这就是我的困惑所在。我只看到
enum Singleton{INSTANCE}
这么多,但不知道如何调用和初始化object您可以在代码中的任何地方使用
Singleton.INSTANCE
。以线程安全方式调用一次的代码,用于初始化此实例,它将位于
enum
@PeterLawrey的构造函数中。您是否忘记了代码片段中的私有构造函数Singleton()?
public class MyClass {
    private static class MyClassInit {
        public static final MyClass instance = new MyClass();
    }

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