使用AtomicReference正确实现延迟初始化单例吗?如果不是 - 可能存在哪些问题?
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicReference;
public class Singleton implements Serializable {
private static final Singleton _instance = new Singleton();
private static AtomicReference<Singleton> instance = new AtomicReference<Singleton>();
private Singleton() {
}
public static Singleton getInstance() {
if (instance.compareAndSet(null, _instance)) {
synchronized (_instance) {
_instance.init();
instance.set(_instance);
}
}
return instance.get();
}
private void init() {
// do initialization
}
private Object readResolve() throws ObjectStreamException {
return getInstance();
}
}
答案 0 :(得分:10)
不,这很糟糕:
public static Singleton getInstance() {
// new "singleton" for every method call
Singleton s = new Singleton();
^^^^^^^^^^^^^^
if (instance.compareAndSet(null, s)) {
synchronized (s) {
s.init();
}
}
return instance.get();
}
使用AtomicReference是一个不错的主意,但它不起作用,因为Java没有懒惰的评估。
经典的1.5单例方法是:
渴望单身人士:
public final class Singleton{
private Singleton(){}
private static final Singleton INSTANCE = new Singleton();
public Singleton getInstance(){return INSTANCE;}
}
Lazy Singleton with inner holder class:
public final class Singleton{
private Singleton(){}
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
public Singleton getInstance(){return Holder.INSTANCE;}
}
Enum Singleton:
public enum Singleton{
INSTANCE;
}
你应该坚持使用其中一个
答案 1 :(得分:1)
你有一个竞争条件,因为你可以在调用Singleton
之前返回init
的实例。如果你想要一次性init
,你可以包装单身。然而,我们知道如何以简单,有效的方式实现单身,可变的单身人士是纯粹的邪恶......
答案 2 :(得分:0)
您可以阻止序列化场景强制单独使用。
所有构造函数都应该是私有的。
答案 3 :(得分:0)
我不明白为什么你需要使用AtomicReference
作为单身人士:AtomicReference
允许你原子地改变对该对象的引用 - 在单身人士的情况下应该有只有一个实例,没有人应该能够再次更改您的应用程序的执行。
此外,您的代码未同步,因此并发请求最终将创建Singleton
类的多个实例(例如@Sean Patrick Floyd所指出的)。
答案 4 :(得分:0)
public static Singleton getInstance() {
Singleton s=instance.get();
if(s!=null)synchronized(s){return s;}//already initialised ->return it
Singleton s = new Singleton();
synchronized(s){
if(instance.compareAndSet(null, s))//try to set
s.init();
}
return instance.get();//use the one that is there after CaS (guaranteed not null)
}
答案 5 :(得分:0)
更新的代码使用readResolve
添加反序列化。
这里有两个明显的问题。
在调用readResolve之前,后引用可以读取原始对象。
即使该类只有一个私有构造函数,它也不是final
。 final
非常重要。手工制作(或使用jig Singleton
实现创建)八位位组序列可以反序列化为子类。 (反序列化机制调用最派生的非可序列化基类的no-args构造函数(必须可由最基本的可序列化类访问)。)不会为子类调用不可访问的readResolve
。