双重检查机制被破解的声明
2017-02-17 15:57
260 查看
在单线程下获取单例的都代码如下所示:
但在多线程模式下,上述代码会出现错误,于是出现了下面所示的代码:
上面的代码每次调用getHelper()是都会进行同步,双重检查机制则试图在变量helper被分配时避免同步。
但是上述代码并不能在多线程环境下正确的实现单例,最主要的原因是对上述语句helper=new Helper();,Help对象的初始化和对helper变量赋值的顺序会错乱,这是由于java内存模型导致的。
假设上述代码执行一下时间序列:
1、线程 1 进入
getHelper() 方法。
2、由于 helper为 null,线程 1 在 //1 处进入 synchronized 块。
3、线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非 null。
4、线程 1 被线程 2 预占。
5、线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将 instance 引用返回给一个构造完整但部分初始化了的 Helper对象。
6、线程 2 被线程 1 预占。
7、线程 1 通过运行 Helper 对象的构造函数并将引用返回给它,来完成对该对象的初始化。
为展示此事件的发生情况,假设代码行helper=new Helper(); 执行了下列伪代码:
mem = allocate(); //为单例对象分配内存空间.
instance = mem; //注意,instance 引用现在是非空,但还未初始化
ctorSingleton(instance); //为单例对象通过instance调用构造函数
还有一种利用ThreadLocal来修复双重检查机制的方法:
参考文章:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html http://www.iteye.com/topic/652440 http://blog.csdn.net/dl88250/article/details/5439024
// Single threaded version class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) helper = new Helper(); return helper; } // other functions and members... }
但在多线程模式下,上述代码会出现错误,于是出现了下面所示的代码:
// Correct multithreaded version class Foo { private Helper helper = null; public synchronized Helper getHelper() { if (helper == null) helper = new Helper(); return helper; } // other functions and members... }
上面的代码每次调用getHelper()是都会进行同步,双重检查机制则试图在变量helper被分配时避免同步。
// Broken multithreaded version // "Double-Checked Locking" idiom class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) synchronized(this) { //1 if (helper == null) //2 helper = new Helper(); //3 } return helper; } // other functions and members... }
但是上述代码并不能在多线程环境下正确的实现单例,最主要的原因是对上述语句helper=new Helper();,Help对象的初始化和对helper变量赋值的顺序会错乱,这是由于java内存模型导致的。
假设上述代码执行一下时间序列:
1、线程 1 进入
getHelper() 方法。
2、由于 helper为 null,线程 1 在 //1 处进入 synchronized 块。
3、线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非 null。
4、线程 1 被线程 2 预占。
5、线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将 instance 引用返回给一个构造完整但部分初始化了的 Helper对象。
6、线程 2 被线程 1 预占。
7、线程 1 通过运行 Helper 对象的构造函数并将引用返回给它,来完成对该对象的初始化。
为展示此事件的发生情况,假设代码行helper=new Helper(); 执行了下列伪代码:
mem = allocate(); //为单例对象分配内存空间.
instance = mem; //注意,instance 引用现在是非空,但还未初始化
ctorSingleton(instance); //为单例对象通过instance调用构造函数
还有一种利用ThreadLocal来修复双重检查机制的方法:
class Foo { /** If perThreadInstance.get() returns a non-null value, this thread has done synchronization needed to see initialization of helper */ private final ThreadLocal perThreadInstance = new ThreadLocal(); private Helper helper = null; public Helper getHelper() { if (perThreadInstance.get() == null) createHelper(); return helper; } private final void createHelper() { synchronized(this) { if (helper == null) helper = new Helper(); } // Any non-null value would do as the argument here perThreadInstance.set(perThreadInstance); } }这个问题在 J2SE 5.0 中已经被修复,可以使用 volatile 关键字来保证多线程下的单例,但是在之前的版本是无效的,需要注意。
// Works with acquire/release semantics for volatile // Broken under current semantics for volatile class Foo { private volatile Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) helper = new Helper(); } } return helper; } }但是最推荐的完美单例方法是:
public class Something { private Something() {} private static class LazyHolder { private static final Something INSTANCE = new Something(); } public static Something getInstance() { return LazyHolder.INSTANCE; } }
参考文章:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html http://www.iteye.com/topic/652440 http://blog.csdn.net/dl88250/article/details/5439024
相关文章推荐
- 双重检查加锁机制--解决线程安全
- 单例模式中用volatile和synchronized来满足双重检查锁机制
- 双重检查锁机制
- 单例模式中用volatile和synchronized来满足双重检查锁机制
- 双重检查加锁机制
- 双重检查加锁机制
- 【分析】声明“双重检查锁坏了”
- 单例模式中用volatile和synchronized来满足双重检查锁机制
- 利用双重检查加锁机制实现线程安全的单例模式
- linux内核模块的版本检查机制
- JAVA 单例双重检查(double check)为什么不好用
- 解析 Linux 内核可装载模块的版本检查机制
- Java 中的双重检查(Double-Check)
- maven项目install时候一直报错,检查Maven-javadoc-plugin声明错误(Java 8 配置Maven-javadoc-plugin)
- 双重检查锁定及单例模式
- 双重检查锁定及单例模式
- 双重检查锁定与延迟初始化(转自infoq)
- 学会软件破解,让注册机制形同虚设
- C++和双重检查锁定模式(DCLP)的风险
- DLINQ 使用DataContext快速构建数据访问层DAL,发现Updata采用Attach(Entity t,true)困难重重!(如果实体声明了版本成员或者没有更新检查策略,则只能将它附加为没有原始状态的已修改实体)的解决办法!