JUC并发编程 原理之 volatile -- double-checked locking(简介 & 问题分析 & 问题解决)
Posted Z && Y
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC并发编程 原理之 volatile -- double-checked locking(简介 & 问题分析 & 问题解决)相关的知识,希望对你有一定的参考价值。
1. 简介
以上的实现特点是:
- 懒惰实例化(只实例化一次)
- 首次使用 getInstance() 才使用 synchronized 加锁,后续使用时无需加锁
- 有隐含的,但很关键的一点:第一个 if 使用了 INSTANCE 变量,是在同步块之外
2. 问题分析
但在多线程环境下,上面的代码是有问题的,getInstance 方法对应的字节码为:
0: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
3: ifnonnull 37
6: ldc #3 // class cn/itcast/n5/Singleton
8: dup
9: astore_0
10: monitorenter
11: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
14: ifnonnull 27
17: new #3 // class cn/itcast/n5/Singleton
20: dup
21: invokespecial #4 // Method "<init>":()V
24: putstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
27: aload_0
28: monitorexit
29: goto 37
32: astore_1
33: aload_0
34: monitorexit
35: aload_1
36: athrow
37: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;
40: areturn
其中:
- 17 表示创建对象,将对象引用入栈 // new Singleton
- 20 表示复制一份对象引用 // 引用地址
- 21 表示利用一个对象引用,调用构造方法
- 24 表示利用一个对象引用,赋值给 static INSTANCE
也许 jvm 会优化为:先执行 24,再执行 21。如果两个线程 t1,t2 按如下时间序列执行:(即先进行赋值操作,再调用构造方法,那么这时候就会得到一个未初始化的单例。)
3. 问题解决
对 INSTANCE 使用 volatile 修饰即可,可以禁用指令重排(写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中),但要注意在 JDK 5 以上的版本的 volatile 才会真正有效。
以上是关于JUC并发编程 原理之 volatile -- double-checked locking(简介 & 问题分析 & 问题解决)的主要内容,如果未能解决你的问题,请参考以下文章
JUC并发编程 共享模式之工具 JUC ReentrantLock -- ReentrantLock原理
JUC并发编程 共享模型之内存 -- Java 内存模型 & 原子性 & 可见性(可见性问题及解决 & 可见性 VS 原子性 & volatile(易变关键字))(代码