Java中的单例设计模式

Sun*_*pta 0 java multithreading

Singleton中的Double Lock检查通常写为:

public static Singleton getInstance()
{ 
    if (instance == null)
    {
        synchronized(Singleton.class) {  //1
            if (instance == null)          //2
                  instance = new Singleton();  //3
        }
    }
    return instance; //4
} 
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,假设有十个线程正在调用此方法,所有这些线程都超过了第一个if条件,然后一个线程进入synchronized块并创建实例.即使创建了实例,它们也需要等待并顺序通过synchronized块,剩下的9个线程将逐个出现.我希望只要任何线程创建Singleton实例,所有其他线程就不应该等待.告诉我是否有解决方案?

Ted*_*opp 8

如果您坚持使用延迟实例化,我认为没有解决方案.您可以在声明instance变量时创建单例对象:

class Singleton {
    private static final instance = new Singleton();

    private Singleton() {} // prevent outside construction

    public static Singleton getInstance() {
        return instance; // no synchronization needed
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢eSniff的评论(以及yair的评论让我对eSniff的评论说得对),这里是维基百科中发布的一种线程安全和懒惰的方法:

class Singleton {
    private static class Holder {
        static final instance = new Singleton();
    }

    private Singleton() {} // prevent outside construction

    public static Singleton getInstance() {
        return Holder.instance; // no synchronization needed
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @TedHopp - 加载__holder__类后立即创建实例.根据这种模式,持有者类只在调用getter时加载,这使得它进行惰性初始化(忽略黑客攻击:)). (2认同)

yai*_*air 8

您是否测试了性能并得出了确实需要延迟初始化的明确结论?如果是这样,请使用持有人模式:

public static class Singleton {
    private static class InstanceHolder {
        public static Singleton instance = new Singleton();
    }

    private Singleton(){}

    public static Singleton getInstance() { 
        return InstanceHolder.instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果你没有经过不错的性能测试,那么最简单的事情就是在其实例声明(急切初始化)中初始化单例,如下所示:

public static class Singleton {
    public static Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance() { 
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

这两种模式允许依赖类加载过程来确保任何使用Singleton视图的线程都是一致的实例.这样您就可以获得两个好处:代码更易读,运行速度更快.

顺便说一下,Double-Check-Idiom不是线程安全的,除非你Singleton.instance声明volatile.