双重检查锁定失败可能性
双重检查锁定在延迟初始化的单例模式中见得比较多(单例模式实现方式很多,这里为说明双重检查锁定问题,只选取这一种方式),先来看一个版本:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
上面是最原始的模式,一眼就可以看出,在多线程环境下,可能会产生多个Singleton实例,于是有了其同步的版本:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public synchronized static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
在这个版本中,每次调用getInstance都需要取得Singleton.class上的锁,然而该锁只是在开始构建Singleton 对象的时候才是必要的,后续的多线程访问,效率会降低,于是有了接下来的版本:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
很好的想法!不幸的是,该方案也未能解决问题之根本:
原因在于:初始化Singleton 和 将对象地址写到instance字段 的顺序是不确定的。在某个线程new Singleton()时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化;此时若另外一个线程来调用getInstance,取到的就是状态不正确的对象。
鉴于以上原因,有人可能提出下列解决方案:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null) {
Singleton temp;
synchronized(Singleton.class) {
temp = instance;
if(temp == null) {
synchronized(Singleton.class) {
temp = new Singleton();
}
instance = temp;
}
}
}
return instance;
}
}
该方案将Singleton对象的构造置于最里面的同步块,这种思想是在退出该同步块时设置一个内存屏障,以阻止初始化Singleton 和 将对象地址写到instance字段 的重新排序。
不幸的是,这种想法也是错误的,同步的规则不是这样的。退出监视器(退出同步)的规则是:所以在退出监视器前面的动作都必须在释放监视器之前完成。然而,并没有规定说退出监视器之后的动作不能放到退出监视器之前完成。也就是说同步块里的代码必须在退出同步时完成,而同步块后面的代码则可以被编译器或运行时环境移到同步块中执行。
编译器可以合法的,也是合理的,将instance = temp移动到最里层的同步块内,这样就出现了上个版本同样的问题。
在JDK1.5及其后续版本中,扩充了volatile语义,系统将不允许对 写入一个volatile变量的操作与其之前的任何读写操作 重新排序,也不允许将 读取一个volatile变量的操作与其之后的任何读写操作 重新排序。
在jdk1.5及其后的版本中,可以将instance 设置成volatile以让双重检查锁定生效,如下:
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
需要注意的是:在JDK1.4以及之前的版本中,该方式仍然有问题。
分享到:
相关推荐
主要介绍了java双重检查锁定的实现方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
单例创建模式是一个通用的编程习语。和多线程一起使用时,必需使用某种类型的...这些事实将导致代码失败,原因是双重检查锁定难于跟踪。在本文余下的部分里,我们将详细介绍双重检查锁定习语,从而理解它在何处失效。
双重检查锁----新旧内存模型的对比!!JDK1.5是新的内存模型
双重预防体系建设检查表.doc
行业资料-交通装置-一种抽油机的双重刹车锁定装置.zip
双重互锁正反转控制电路图 如下图所示,图中SB2和SB3均为复合按钮,合上电源开关Q,按下起动按钮SB2,其常闭触点SB2断开,使接触器KM2不得电;常开触点SB2接通,使接触器KM1得电吸合并自锁,其主触点闭合,接通电源...
今天小编就为大家分享一篇关于Java双重检查加锁单例模式的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
电子政务-双重锁定式电连接器.zip
本文主要讲了一下关于电动机双重互锁正反转接线图,希望对你的学习有所帮助。
双重联锁正反转控制电路图.pdf
双重联锁正反转控制线路PPT课件.pptx
《按钮、接触器双重联锁正反转控制线路》学案.pdf
行业资料-电子功用-具有双重锁定件的电连接器的介绍分析.rar
电子政务-兆瓦级风力发电机组具有双重锁定功能的主轴锁.zip
双重否定句和肯定句的转换[文].pdf
行业资料-电子功用-具有双重锁定功能的电器电源控制装置的介绍分析.rar
接触器、按钮双重连锁正反转控制电路图 如下图为常见的正反转控制电路,通过接触器、按钮双重联锁对电路进行保护。 电路主要元器件及其作用 1、空气开关(QS):电源的通断,有短路、严重过载及欠电压保护2、熔...
双重预防体系建设检查表.pdf