Java 使用原子引用的单例
使用AtomicReference实现惰性初始化singleton是否正确?如果没有-可能存在哪些问题Java 使用原子引用的单例,java,singleton,Java,Singleton,使用AtomicReference实现惰性初始化singleton是否正确?如果没有-可能存在哪些问题 import java.io.ObjectStreamException; import java.io.Serializable; import java.util.concurrent.atomic.AtomicReference; public class Singleton implements Serializable { private static final Sin
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();
}
}
import java.io.ObjectStreamException;
导入java.io.Serializable;
导入java.util.concurrent.AtomicReference;
公共类单例实现了可序列化{
私有静态最终单例_实例=新单例();
私有静态AtomicReference实例=新的AtomicReference();
私人单身人士(){
}
公共静态单例getInstance(){
if(instance.compareAndSet(null,_instance)){
已同步(\u实例){
_init();
instance.set(_instance);
}
}
返回instance.get();
}
私有void init(){
//初始化
}
私有对象readResolve()引发ObjectStreamException{
返回getInstance();
}
}
您可以阻止序列化场景,从而强制使其成为单例
所有构造函数都应该是私有的。不,这很糟糕:
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没有惰性计算
经典的Post1.5单例方法有: 热切的单身汉:
public final class Singleton{
private Singleton(){}
private static final Singleton INSTANCE = new Singleton();
public Singleton getInstance(){return INSTANCE;}
}
public final class Singleton{
private Singleton(){}
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){return Holder.INSTANCE;}
}
public enum Singleton{
INSTANCE;
}
具有内部holder类的惰性单例:
public final class Singleton{
private Singleton(){}
private static final Singleton INSTANCE = new Singleton();
public Singleton getInstance(){return INSTANCE;}
}
public final class Singleton{
private Singleton(){}
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){return Holder.INSTANCE;}
}
public enum Singleton{
INSTANCE;
}
枚举单例:
public final class Singleton{
private Singleton(){}
private static final Singleton INSTANCE = new Singleton();
public Singleton getInstance(){return INSTANCE;}
}
public final class Singleton{
private Singleton(){}
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){return Holder.INSTANCE;}
}
public enum Singleton{
INSTANCE;
}
您可能应该坚持使用其中的一个我不明白为什么需要对单例使用
AtomicReference
:AtomicReference
允许您以原子方式更改对对象的引用——对于单例,应该只有一个实例,任何人都不能再更改它用于执行您的应用程序。
此外,您的代码没有同步,因此并发请求最终将创建多个
Singleton
类的实例(例如@Sean Patrick Floyd指出)。您有一个竞争条件,在调用init
之前,您可以返回一个Singleton
的实例。如果您想要一个只使用一次的init
,那么可以包装singleton。然而,我们知道如何以简单、高效的方式实现单例,而可变单例是纯粹的邪恶 更新的代码使用readResolve
添加反序列化
这里有两个明显的问题
- 在调用readResolve之前,反向引用可以读取原始对象
- 即使该类只有一个私有构造函数,它也不是
<代码>最终版非常重要。手工制作(或使用jigfinal
implementation创建)的八位字节序列可以反序列化为子类。(反序列化机制调用派生最多的不可序列化基类(必须可供最基本的可序列化类访问)的无参数构造函数。)子类不调用不可访问的Singleton
readResolve
公共单例getInstance(){return INSTANCE;}
不是线程安全的。此外,如果他们序列化它以获得另一个,也可以对其进行黑客攻击object@Jigar:是的,它是线程安全的,在执行方法之前初始化静态字段,但是关于序列化攻击,您是对的,请使用“枚举单例”。它是最健壮的。IIRC对序列化攻击免疫我想我已经解决了。你能看一看这篇文章吗?可能单独的init
的想法是你只运行该部分一次。你也可以把它放在CaS之后,让init检查它是否已经被调用,但我真的找不到一个好的线程安全方法来做这件事(CaS成功后,另一个线程可以得到未初始化的单例)nvm找到了oneUm,我确信这仍然是一场代码竞赛。这真的很难看,而且完全没有必要。我明白了:用getInstance替换最后一个get,将不会返回未初始化的单例,尽管这确实是一个很大的难题,使用类加载语义就足以解决这个问题。在答案发布后更改问题中的代码可能不太好。@Sean Patrick Floyd,但新代码更清楚地说明了它仍然存在的问题。尽管它确实在混合中添加了反序列化。