关于java:Singleton设计模式坏了吗?

Singleton design pattern broken?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Efficient way to implement singleton pattern in Java

我本以为下面的类是线程安全的单线程类,但阅读http://taskinoor.wordpress.com/2011/04/18/singleton_multiphreaded/似乎不是。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ThreadSafeSingleton {

    private static ThreadSafeSingleton ref;

    private ThreadSafeSingleton(){

    }

    public ThreadSafeSingleton getSingletonObject(){

        if(ref == null){
            ref = new ThreadSafeSingleton();
        }  
        return ref;

    }
}

根据文章,唯一真正的线程安全单例是-

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ThreadSafeSingleton {

    private static ThreadSafeSingleton ref = new ThreadSafeSingleton();

    private ThreadSafeSingleton(){

    }

    public ThreadSafeSingleton getSingletonObject(){
        return ref;

    }
}

这是正确的吗?


这不是唯一的线程安全单例,但这是正确的。另一种方法是同步在第一个示例中创建单例实例的代码。但问题在于,您必须同步代码,这可能是问题,也可能不是问题。另一个可能的问题是singleton没有被延迟初始化。同样,根据体系结构和需求,它可能是问题,也可能不是问题。还有另一个奇怪的模式来处理这个问题:http://en.wikipedia.org/wiki/initialization_on_demand_holder_习语。


是的,文章是正确的。

在上面的示例中,如果同时由两个函数调用该方法,则它们可能都将ref视为空,因为在其他函数检查它之前,它们都没有实际完成创建和分配。

在下面的示例中,当类被加载时,ref被分配一次,然后任何东西都可以访问它。


是的,是正确的。

在第一种情况下,如果两个线程同时调用getSingletonObject(),则有可能最终得到两个单例实例。

在第二种情况下,该方法只返回对在类加载期间创建的现有对象的引用,这是由JVM以线程安全的方式完成的。

在线程安全性方面,第二个比第一个要好得多。


实现ThreadSafe单例对象有几种不同的方法。静态初始化(底部方法)就是其中之一。

还可以使用静态内部类、依赖项注入、同步EDCOX1×5、甚至是Java 5 +中的双重检查锁定模式,只要HER变量被声明为EDCOX1×6。有关更多信息,请参见实践中的Java并发。

其中,如果必须构建实际的单例,我更喜欢第一种方法。否则,我喜欢以图形的形式使用范围。


1
2
3
4
5
6
if(ref == null)
{
    ref = new ThreadSafeSingleton();
}            

return ref;

两个线程在大致相同的时间运行可能会撞击if (ref == null)。两个线程可以看到ref实际上是空的。然后两个线程将创建一个新实例。然后可以为两个线程提供单独的实例。现在您有了两个不同的"singleton"对象实例。

在这段代码中没有任何机制可以阻止上面描述的竞争条件。