关于作者:CSDN内容合作伙伴,技术专家, 从零开始做日常生活的千万级APP。
专注于在各个领域分享一系列原创文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。
目录。
- 一、导读。
- 二、概览。
- 2.1 作用。
- 2.2 多线程共享变量的访问过程。
- 2.3 为什么多线程会出现可见性问题?
- 2.4 如何实现volatile的可见性?
- 2.5 禁止指令如何重新排序?
- 2.6 举例。
- 2.7 来答题。
- 三、原理。
- 四、 推荐阅读。
一、导读。
继续学习Java基础知识,温故知新。
二、概览。
volatile Java关键字,可用于修改变量volatile又称轻量级synchronized,运行时的开销比 synchronized更小。
2.1 作用。
1、确保共享变量在线程间的同步,实现可见性。
2、禁止处理器重排序。
2.2 多线程共享变量的访问过程。
线程执行时首先将主存数据复制到本线程本地操作完成后,将结果从线程本地刷到主存。
请看下图,多线程时间共享变量操作的值在红色区域。

2.3 为什么多线程会出现可见性问题?
由CPU缓存引起的可见性,CPU 增加缓存,以平衡和内存的速度差,导致 可见性问题。
在多线程环境下,多个线程同时访问共享变量,由于不同线程的执行顺序和时间不确定,在其他线程中,一个线程对共享变量的修改可能是看不见的。可以参考上述共享变量的访问流程。
2.4 如何实现volatile的可见性?
volatile不允许在线程中进行缓存和重排序c;直接修改内存因此,其他线程是可见的。
volatile修改的变量读写时,都会直接刷主存,因此,变量是可见的。
2.5 禁止指令如何重新排序?
volatile通过编译器生成字节码,将“内存屏障”添加到指令序列中,以禁止指令重新排序。
当一个变量被修改时,#xff0c;表示变量为“易变”(volatile)或“不稳定”(unstable),这意味着该变量的值可能是其他线程(或者过程修改。
注意:volatile 关键字只能保证线程之间的同步,不能保证线程安全。。
确保线程安全,需要使用其它同步机制༌比如 synchronized 关键字或者 Lock 接口。
volatile有。可见性、有序性。,但不具有原子性,因此,线程不安全。
2.6 举例。
禁止线程缓存变量结果。可见性问题主要是指共享变量值的线程修改c;另一个线程看不见。可见性问题的主要原因是每个线程都有自己的高速缓存区——线程工作内存。例:// Thread-A。new。Thread。(。"Thread A")。{ 。singleton。 =new。Singleton。(。)。;}。}。}。return。singleton。;}。}。一个对象实际上可以分为三个步骤:*。内存空间的分配。*。对象的初始化。*。相应引用内存空间的地址。但是,由于操作系统可以重新排序指令,因此,上述过程也可能变成以下过程:*。内存空间的分配。*。相应引用内存空间的地址。*。如果初始化对象是这个过程,未初始化的对象可能会在多线程环境中暴露出来,导致不可预测的结果。所以,为了防止这个过程的重新排序,我们需要将变量设置为。volatile。类型变量。
对。volatile。单读变量。/。写作操作可以保证原子性,如。long。和。double。类型变量但是i不能保证。++这种操作的原子性,因为本质上i。++读写两次操作,确保多步原子性,可以通过。AtomicInteger。或者。Synchronized。实现,本质上是cas操作。
2.7 来答题。
i。++其实是复合操作,包括三步:*。读取i的值。*。对i加。1。。*。将i的值写回内存。 volatile。不能保证这三个操作是原子,可以通过。AtomicInteger。或者。Synchronized。来保证。+1。操作原子性。
- 问题2: 为什么要使用volatile共享long和double变量?
因为。long。和。double。两种数据类型的操作可分为高。32。位和低。32。两部分,所以很普通。long。或。double。类型读。/。写作可能不是原子。所以,鼓励大家分享。long。和。double。变量设置为。volatile。类型,这样可以保证在任何情况下都是对的。long。和。double。的单次读。/。所有的写作操作都有原子性。
三、原理。
采用“内存屏障”实现JVM底层volatile,加入volatile关键字将会有更多的lock前缀指令。
内存屏障又称内存栅栏是一个 CPU 指令。
1、编译生成使用javac命令.class文件,
2、然后用javap命令反编译查看.class文件信息,在字节码信息中可以看到更多的指令。
为确保每个处理器的缓存是一致的,实现了缓存一致性协议(MESI),每个处理器通过嗅探在总线上传播的数据来检查缓存值是否过期,当处理器发现缓存行对应的内存地址被修改,将当前处理器的缓存行设置为无效状态,当处理器修改此数据时,将数据从系统内存读取到处理器缓存中。
volatile 通过这种机制,每个线程都可以获得该变量的最新值。
volatile 通过这种机制,每个线程都可以获得该变量的最新值。
3.2 使用场景:
- 解决变量可见性要求,但是对阅读顺序没有要求。
volatile特性:
(1)volatile只能用于变量级别。
(2)volatile只能实现变量修改的可见性,不能保证原子性volatile + cas 原子性实现了c;比如atomic包下面的类。
(3)volatile不会造成线程堵塞。
(4)编译器不会优化volatile标记的变量。
四、 推荐阅读。
【Java基础】原子性、可见性和有序性。
Java可见性[Java基础] Happens-before。
[Java基础]java-Synchronized面试android。
[Java基础]java-android面试-线程状态。
【Java基础】线程相关。
[Java基础]java 异常。
[Java基础]java 反射。
[Java基础]java 泛型。
java注释[Java基础]。
java动态代理[Java基础]。
[Java基础]Java SPI。
[Java基础]Java SPI 二 之 Java APT。
[Java基础] jvm 堆、堆、堆、方法区 & java 内存模型。