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并发编程 详解Java关键字之 volatile

JUC并发编程 详解Java关键字之 volatile

JUC并发编程 共享模式之工具 JUC ReentrantLock -- ReentrantLock原理

JUC并发编程(10)--- 谈谈对Volatile的理解

JUC并发编程 共享模型之内存 -- Java 内存模型 & 原子性 & 可见性(可见性问题及解决 & 可见性 VS 原子性 & volatile(易变关键字))(代码

Java 并发JUC数据结构CopyOnWriteArrayList原理