单身人士使用AtomicReference

时间:2011-05-09 14:18:43

标签: java singleton

使用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();
    }

}

6 个答案:

答案 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之前,后引用可以读取原始对象。

  • 即使该类只有一个私有构造函数,它也不是finalfinal非常重要。手工制作(或使用jig Singleton实现创建)八位位组序列可以反序列化为子类。 (反序列化机制调用最派生的非可序列化基类的no-args构造函数(必须可由最基本的可序列化类访问)。)不会为子类调用不可访问的readResolve