一篇读懂 Volatile 关键字
一、volatile 简介
volatile 是 java 的一个关键字,也是面试经常问的一个热门知识,它为 Java 虚拟机提供的轻量级的同步机制。
主要有三个特性:1.保证可见性。2.不保证原子性。3.禁止指令重排序。
二、保证可见性
JMM 内存模型之可见性:即当线程完成对于共享变量的操作并写回主内存后,主内存在第一时间通知其他相关线程共享变量发生变化的机制
早期:对共享变量加一把总线锁
现在:MESI 缓存一致性协议,CPU 总线嗅探机制(监听机制)
①如果线程内的共享变量发生改变,则当前处理器的缓存立即同步到主内存,缓存行锁定
②触发 MESI 缓存一致性,线程工作内存共享变量失效。
具体看一个例子:
不加 volatile 时,打印:修改 flag 值为:false
加了 volatile 后,打印:
三、不保证原子性
JMM 内存模型之原子性:不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或分割,需要整体完整,要么同时成功,要么同时失败。
单个基本数据类型的基本运算可以看作是保证原子性的,但多个基本数据类型运算不能保证。比如:i++;
解决方法:
直接使用原子类计算:利用 java.util.concurrent.atomic.AtomicInteger 类
四、禁止指令重排序
指令重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,有时候会改变程序语句的先后顺序。不存在数据依赖关系,可以重排序; 存在数据依赖关系,禁止重排序 。但重排后的指令绝对不能改变原有的串行语义。
经典重排序案例:
volatile 通过内存屏障保证可见性和有序性,内存屏障:Java 内存模型的重排规则会要求 Java 编译器在生成 JVM 指令时插入特定的内存屏障指令。
主要的四大指令:loadload()、storestore()、loadstore()、storeload( )
volatile 关键字影响的是 Class 内的 Field 的 flags :添加了一个 ACC_ _VOLATILE,JVM 在把字节码生成为机器码的时候,发现操作是 volatile 的变量的话,就会根据 JMM 要求,在相应的位置去插入内存屏障指令。
五、Volatile 与 Synchronized 比较
Volatile 是轻量级的 synchronized,因为它不会引起上下文的切换和调度,所以 Volatile 性能更好。
Volatile 只能修饰变量,synchronized 可以修饰方法,静态方法,代码块。
Volatile 对任意单个变量的读/写具有原子性,但是类似于 i++这种复合操作不具有原子性。而锁的互斥执行的特性可以确保对整个临界区代码执行具有原子性。
多线程访问 volatile 不会发生阻塞,而 synchronized 会发生阻塞。
volatile 是变量在多线程之间的可见性,synchronize 是多线程之间访问资源的同步性。
评论