Singleton设计模式
双锁检查通常写为:Singleton设计模式
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance; //4
}
在上面的代码中,假设10个线程调用这个方法,所有的人都越过了第一个if条件,那么一个线程进入同步块并创建实例。即使创建了实例,剩余的9个线程也会一个接一个地等待,并通过synchronized块顺序进入。我希望一旦任何线程创建Singleton实例,所有其他线程都不应该等待。告诉我是否有解决方案?
如果你坚持使用懒惰实例,我不认为有一个解决方案。
你可以只创建单独的对象,当你声明instance
变量:
class Singleton {
private static final instance = new Singleton();
private Singleton() {} // prevent outside construction
public static Singleton getInstance() {
return instance; // no synchronization needed
}
}
多亏了eSniff注释(由亚伊尔注释,并把我说得对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
}
}
此解决方案是线程安全和懒惰的。 http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – eSniff 2011-12-22 06:09:46
感谢您的快速回复Ted,您是否知道使用Concurrent包类实现它的任何其他方法? – 2011-12-22 06:10:41
@eSniff - 这不是懒惰:只要加载类,就会创建单例,这可能在调用getInstance()之前就已经完成了。 – 2011-12-22 06:19:47
如果10线程使用此Singleton对象在同一时间,然后它会创建操纵每个线程在同一时间修改此对象的问题。为了避免这种情况,我们使用synchronized关键字,因此一次只有一个线程可以访问Singleton类的对象。如果你希望所有的线程都可以同时访问这个对象,那么删除synchronized关键字。
这并不完全正确。唯一的同步是围绕创建单例的双重检查锁定。在创建单例之后,调用'getInstance()'不需要任何同步。 – 2011-12-22 06:11:08
我可以通过调整类加载器来更改单例的行为吗?这个问题在一次采访中被问到了我。你们有没有直接面试官试图指出的想法? – 2011-12-22 06:30:52
您是否测试过性能,并得出确实需要延迟初始化的确切结论?如果是这样,使用持有者模式:
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
意见一致的情况下任何线程。这样你就可以获得两个好处:代码更具可读性,运行速度更快。
顺便说一句,Double-Check-Idiom不是线程安全的,除非您的Singleton.instance
被宣布为volatile
。
我可以通过调整类加载器来更改Singleton的行为吗?这个问题在一次采访中被问到了我。你们有没有直接面试官试图指出的想法? – 2011-12-22 06:31:17
我不确定我是否理解“需求”,但是您可以使用不同的类加载器加载单例,从而在JVM中拥有多个单例实例... – yair 2011-12-22 06:37:55
Bill Pugh是你的英雄:http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – eSniff 2011-12-22 06:03:16
@Mitch:那篇文章已经9岁了。双重检查锁定可以在任何非古董虚拟机中安全工作;但它不是实例化单例的最佳方式。 – 2011-12-22 06:11:53
@Mitch:啊!这太糟糕了,只要你知道它今天不是直接适用的,它仍然是一个很好的和信息丰富的阅读。这里又是:http://www.ibm.com/developerworks/java/library/j-dcl/index.html。随后讨论了在新内存模型下安全双重检查锁定的缺点:http://www.ibm.com/developerworks/library/j-jtp03304/#3.2 – 2011-12-22 06:22:59