您的位置:首页 > 其它

二十二、应用双重锁定检查于单例模式中的问题

2012-08-27 16:18 281 查看
之前在很多单例类中看到双重锁定检查(DCL),也听到过两种声音:第一种声音是希望只在第一次创建实例时进行同步,于是才有两次判断instance是否为null的判断;另一种声音是双重锁定检查用在这里根本起不到预想的作用。今天终于知道后一种说法的原因了。

public class SingletonPattern {
private static SingletonPattern instance = null;
private SingletonPattern(){
}
public static SingletonPattern getInstance(){
if(instance == null){
synchronized(SingletonPattern.class){
if(instance == null){
instance = new SingletonPattern();
}
}
}
return instance;
}
}
看上去貌似是只有在对象还不存在时即第一次创建对象时才会走到synchronized(SingletonPattern.class),instance已经存在了的话,在第一个if(instance == null)时就不满足所以不会进入synchronized(SingletonPattern.class)。真的是这样吗?
首先来看这句instance = new SingletonPattern();这条语句被编译后形成了多条汇编指令,主要做如下三个动作:
1.给SingletonPattern的实例分配内存。
2.初始化SingletonPattern的构造器。
3.将instance对象只想分配的内存空间。
这些汇编指令在JVM中执行,由于Java编译器允许处理器乱序执行,所以上面的动作2和3的顺序是无法保证的,有可能是123也可能是132。如果是132的情况,如果第一个线程在3执行完2未执行前,就被切换到第二个线程上,这时instance因为已经在线程一中执行了动作3,instance已经是费空了,所以线程二直接用这个instance,而这个instance实际上还没有被构造完成,就会出错了。
如果一定要用双重锁定检查来实现单例模式,那在JDK 1.5及之后版本中,可以将instance的声明改成private volatile static SingletonPattern instance = null;就是加了个volatile,这就保证每次用instance都是从主内存中读取,这样就可以使用双重锁定检查来完成单例模式了。当然volatile或多或少也会影响到性能。所以索性不如用下面这种实现了:
public class SingletonPattern2 {
private static final SingletonPattern2 singletonPattern = new SingletonPattern2();
private SingletonPattern2(){
}
public synchronized static SingletonPattern2 getInstance(){
return singletonPattern;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: