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
}
在上面的代码中,假设有10个线程正在调用此方法,所有这些线程都超过了第一个if条件,然后一个线程进入synchronized块并创建实例。即使创建了实例,它们也需要等待并顺序通过synchronized块,剩下的9个线程将逐个出现。我希望只要任何线程创建Singleton实例,所有其他线程就不应该等待。告诉我是否有解决方案吗?
答案 0 :(得分:8)
如果您坚持使用延迟实例化,我认为没有解决方案。您可以在声明instance
变量时创建单例对象:
class Singleton {
private static final instance = new Singleton();
private Singleton() {} // prevent outside construction
public static Singleton getInstance() {
return instance; // no synchronization needed
}
}
感谢eSniff的评论(以及yair的评论让我对eSniff的评论说得对),这里是Wikipedia中发布的方法,用于线程安全和懒惰的方法:
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
}
}
答案 1 :(得分:8)
您是否测试了性能并得出了您确实需要延迟初始化的明确结论?如果是这样,请使用持有者模式:
public static class Singleton {
private static class InstanceHolder {
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
}
但是,如果你没有经过不错的性能测试,最简单的事情就是在实例声明中初始化单例(急切的初始化),像这样:
public static class Singleton {
public static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
这两种模式允许依赖类加载过程来确保使用Singleton
的任何线程查看一致的实例。这样您就可以获得两个好处:代码更易读,运行速度更快。
顺便说一下,除非Singleton.instance
被声明为volatile
,否则Double-Check-Idiom不是线程安全的。
答案 2 :(得分:0)
如果您的10个线程同时使用此Singleton对象,那么它将在操作中产生问题,每个线程同时修改此对象。为了避免这种情况,我们使用了synchronized关键字,所以一次只有一个线程可以访问Singleton类的对象。如果您希望所有线程可以同时访问此对象,则删除synchronized关键字。