2011-12-22 79 views
0
在辛格尔顿

双锁检查通常写为: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实例,所有其他线程都不应该等待。告诉我是否有解决方案?

+1

Bill Pugh是你的英雄:http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – eSniff 2011-12-22 06:03:16

+0

@Mitch:那篇文章已经9岁了。双重检查锁定可以在任何非古董虚拟机中安全工作;但它不是实例化单例的最佳方式。 – 2011-12-22 06:11:53

+0

@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

回答

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注释(由亚伊尔注释,并把我说得对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 
    } 
} 
+0

此解决方案是线程安全和懒惰的。 http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – eSniff 2011-12-22 06:09:46

+0

感谢您的快速回复Ted,您是否知道使用Concurrent包类实现它的任何其他方法? – 2011-12-22 06:10:41

+0

@eSniff - 这不是懒惰:只要加载类,就会创建单例,这可能在调用getInstance()之前就已经完成了。 – 2011-12-22 06:19:47

0

如果10线程使用此Singleton对象在同一时间,然后它会创建操纵每个线程在同一时间修改此对象的问题。为了避免这种情况,我们使用synchronized关键字,因此一次只有一个线程可以访问Singleton类的对象。如果你希望所有的线程都可以同时访问这个对象,那么删除synchronized关键字。

+0

这并不完全正确。唯一的同步是围绕创建单例的双重检查锁定。在创建单例之后,调用'getInstance()'不需要任何同步。 – 2011-12-22 06:11:08

+0

我可以通过调整类加载器来更改单例的行为吗?这个问题在一次采访中被问到了我。你们有没有直接面试官试图指出的想法? – 2011-12-22 06:30:52

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意见一致的情况下任何线程。这样你就可以获得两个好处:代码更具可读性,运行速度更快。

顺便说一句,Double-Check-Idiom不是线程安全的,除非您的Singleton.instance被宣布为volatile

+0

我可以通过调整类加载器来更改Singleton的行为吗?这个问题在一次采访中被问到了我。你们有没有直接面试官试图指出的想法? – 2011-12-22 06:31:17

+0

我不确定我是否理解“需求”,但是您可以使用不同的类加载器加载单例,从而在JVM中拥有多个单例实例... – yair 2011-12-22 06:37:55