系列文章
Volatile测试案例一可见性
目录
前言
测试1
逻辑
代码
结果
测试2
逻辑
代码
结果
结论
原理探讨(可见性)
前言
多线程是 JAVA 并发编程的主要应用,并发环境能大幅提高应用性能,提高 CPU 使用率,但是并发环境下也引出许多问题。今天咱们主要探讨的就是多线程并发中的一个关键字:Volatile。
Volatile 的主要作用:
- 内存可见性
- 禁止指令重排序
测试1
逻辑
1.创建一个 BOOL 结束标记,用于结束线程
2.启动多个线程(这里 10 )
3.将结束标记改为 false
代码
public class VolatileExample {
private static boolean BOOL = true;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> { while (BOOL) { } },"VolatileThread-"+i).start();
}
TimeUnit.MILLISECONDS.sleep(500);
BOOL = false;
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
String name = runtime.getName();
System.out.println("当前进程的标识为:" + name);
}
}
结果
1.通过 java 自带的 jstack 命令,截取 name 中的 @ 前的数字作为 pid(我这里为 11744 )
2.运行 jstack -l pid
3.当前查看到的线程情况(部分线程)
4.观察当前线程可知部分线程未接收到 BOOL 标记的改变而结束线程
测试2
逻辑
1.在测试1的逻辑基础上,添加 volatile 关键字
代码
public class VolatileExample {
private volatile static boolean BOOL = true;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> { while (BOOL) { } },"VolatileThread-"+i).start();
}
TimeUnit.MILLISECONDS.sleep(500);
BOOL = false;
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
String name = runtime.getName();
System.out.println("当前进程的标识为:" + name);
}
}
结果
1.一旦加上 volatile 关键字,当前进行在极短的时间内结束
结论
1.直观来看,volatile 保证了变量的内存可见性,它使得变量能在不同线程中共享
2.这里线程的开始可以使用栅栏统一控制
原理探讨(可见性)
在 JMM 中,可见性问题存在于主内存、CPU内部多级缓存之间。CPU内部多级缓存与主内存是保持同步的,主要通过缓存⼀致性协议(MESI)来解决,但是一但涉及一致性,这就相当消耗性能。为此,线程又维护了自己的一份本地缓存,这又导致了新的可见性问题,线程本地缓存与主内存不一致。
Volatile 就是为了解决变量不一致的问题的,也就是可见性问题。其做法是:
- 它会强制将对缓存的修改操作立即写入主存;
- 如果是写操作,它会导致其他CPU中对应的缓存行无效
- 如果是读操作,它发现当前缓存失效后直接去主内存中读取