我原本以为下面的类是一个线程安全的单例,但是看起来它不是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;
}
}
这是对的吗?
答案 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 class,dependency injection,同步volatile
,甚至是double-checked locking pattern in Java 5+。有关详细信息,请参阅Java Concurrency in Practice。
其中,如果我必须构建一个实际的单例,我更喜欢第一种方法。否则我喜欢在Guice中使用范围。