目录
-
- 一、volatile 简介
-
- 1.1 定义
- 1.2 volatile 的两个特性
- 二、特性1:保证线程间的可见性
-
- 示例1:普通场景
-
- 1)代码示例:
- 2)执行结果:
- 3)总结:
- 示例2:被 JIT 即时编译器优化
-
- 1)代码示例:
- 2)执行结果:
- 3)原因分析:
- 4)什么是 JIT 即时编译器?
- 4)解决方案一:
- 5)解决方案二:
- 三、特性2:禁止指令重排序
-
- 3.1 场景举例
- 3.2 使用 jcstress 复现指令重排序
- 3.3 volatile 修复指令重排序问题
- 3.4 volatile 防止指令重排的机制
- 3.5 volatile 使用技巧
- 四、补充:什么是指令重排序?
-
- 4.1 指令重排序的好处:提升处理速度
- 4.2 重排序的 3 种情况
-
- 1)编译器优化
- 2)CPU 重排序
- 3)内存的 “重排序”(不是真正的重排序)
一、volatile 简介
1.1 定义
volatile
关键字可以用于修饰我们之前说的共享变量(主内存中存储的对象、成员变量、数组等)。
一旦共享变量被 volatile 修饰后,就具备了两层含义:
- 保证线程间的可见性。
- 禁止指令重排序。
1.2 volatile 的两个特性
特性1:保证线程间的可见性
- 用 volatile 修饰共享变量,能够防止编译器等优化发生,让一个线程修改共享变量的同时对另一个线程可见。
特性2:禁止指令重排序
- 用 volatile 修饰的共享变量,会在读/写共享变量时,加入不同的屏障,阻止其它读写操作越过屏障,从而达到阻止指令重排序的效果。
二、特性1:保证线程间的可见性
- 用 volatile 修饰共享变量,能够防止编译器等优化发生,让一个线程修改共享变量的同时对另一个线程可见。
示例1:普通场景
1)代码示例:
例如下面这段代码:
private static boolean stop = false;
public static void main(String[] args) {
// 线程1
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true;
System.out.println(Thread.currentThread().getName() + ": modify stop to true...");
}, "t1").start();
// 线程2
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + ": " + stop);
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName