单身设计模式破碎了吗?

时间:2011-12-01 21:25:23

标签: java design-patterns singleton

  

可能重复:
  Efficient way to implement singleton pattern in Java

我原本以为下面的类是一个线程安全的单例,但是看起来它不是http://taskinoor.wordpress.com/2011/04/18/singleton_multithreaded/

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton ref;

    private ThreadSafeSingleton(){

    }

    public ThreadSafeSingleton getSingletonObject(){

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

    }
}

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

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton ref = new ThreadSafeSingleton();

    private ThreadSafeSingleton(){

    }

    public ThreadSafeSingleton getSingletonObject(){
        return ref;

    }
}

这是对的吗?

5 个答案:

答案 0 :(得分:4)

这不是唯一的线程安全单例,但这是正确的。另一种方法是在第一个示例中同步创建单例实例的代码。但问题是你必须同步代码,这可能是也可能不是问题。另一个可能的问题是单例不是懒惰地初始化。同样,根据架构和要求,它可能是也可能不是问题。处理该问题还有另一种奇怪的模式:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

答案 1 :(得分:2)

是的,文章是正确的。

在上面的示例中,如果方法被两个函数同时调用,它们可能都会将ref看作null,因为在另一个函数检查它之前,它们都没有实际完成创建和分配。

在底部示例中,ref在加载类时分配一次,然后才能访问它。

答案 2 :(得分:1)

是的,这是正确的。

在第一种情况下,如果两个线程同时调用getSingletonObject(),则存在以两个单例实例结束的风险。

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

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

答案 3 :(得分:0)

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

return ref; 

在大致相同的时间运行的两个线程可能会命中if (ref == null)。两个线程可以看到ref实际上是null。然后两个线程将创建一个新实例。然后,两个线程可以给出单独的实例。现在你有两个不同的实例" singleton"对象

此代码段中没有任何机制可以防止上述竞争条件。

答案 4 :(得分:0)

实现线程安全单例对象有几种不同的方法。静态初始化(底部方法)是这些方法之一。

只要持有人变量被声明为getInstance(),您也可以使用static inner classdependency injection,同步volatile,甚至是double-checked locking pattern in Java 5+。有关详细信息,请参阅Java Concurrency in Practice

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