volatile 关键字
✨个人主页:bit me👇
✨当前专栏:Java EE初阶👇
✨每日一语:人是怎么颓废的?假装试的努力,持久性的幻想,语言上的巨人,行动上的矮子。
目 录
⏰一. volatile
写一串代码:
- 创建两个线程 t1 和 t2
- t1 中包含一个循环, 这个循环以 flag == 0 为循环条件.
- t2 中从键盘读入一个整数, 并把这个整数赋值给 flag.
- 预期当用户输入非 0 的值的时候, t1 线程结束
public class Demo16 {
//写一个内部类,此时这个内部类就能处在 Demo16 的内部,和 Demo14 中 Counter 类不是一个作用域
static class Counter{
public int flag = 0;
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(()->{
while (counter.flag == 0){
//执行循环,但是此处循环啥都不做
}
System.out.println("t1 结束");
});
t1.start();
Thread t2 = new Thread(()->{
//让用户输入一个数字,赋值给 flag
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数:");
counter.flag = scanner.nextInt();
});
t2.start();
}
}
运行结果:
当我们输入了一个 1 之后,t1 线程并没有结束!!这里就涉及到了我们的内存可见性问题(内存改了,线程没看见或者说线程没能及时读取到更新后的数据)
t1 做的工作:LOAD 读内存的数据到 CPU 寄存器,TEST:检查 CPU 寄存器的值是否和预期的一样,这两个动作反复的进行,频繁的进行,读内存比读 CPU 寄存器慢几千上万倍,意味着当前的 t1 主要操作就是慢在 LOAD 上,编译器每次 TEST 的时候都看着 LOAD 的结果都没啥变化,就直接进行了优化,那就是相当于只从内存中读取一次数据然后在寄存器里进行反复 TEST。
上述操作,编译器里的优化我们不好干预,于是对上述场景编译器可能知道自己出现误判,给我们提供了一个关键字 volatile。
volatile 关键字是要写到要修改的变量上的
volatile public int flag = 0;
volatile 操作相当于显式的禁止了编译器进行上述优化,是给这个对应的变量加上了 "内存屏障" (特殊的二进制指令),JVM 在读取这个变量的时候,因为内存屏障的存在,就知道要每次都重新读取这个内存的内容,而不是频繁进行草率的优化。(频繁读取内存,速度是慢了,但是数据算对了)
- 去除 volatile,改变循环里的条件,看看效果
while (counter.flag == 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果:
发现程序又没有在此处做优化,编译器的优化是根据代码实际情况来的,上个代码是循环为空所以转速极快,导致读取内存次数频繁,触发了优化,此处加了 sleep ,转速慢下来了,读取次数减少,没有触发优化了,由于不知道编译器何时该优化何时不该优化,所以必要的时候还是得加上 volatile。
代码在写入 volatile 修饰的变量的时候:
- 改变线程工作内存中volatile变量副本的值
- 将改变后的副本的值从工作内存刷新到主内存
代码在读取 volatile 修饰的变量的时候:
- 从主内存中读取volatile变量的最新值到线程的工作内存中
- 从工作内存中读取volatile变量的副本
线程优化之后,主要在操作工作内存,没有及时读取主内存,导致出现误判
工作内存:不是真正的内存,其实就是 CPU 寄存器(可能还是加上 CPU 缓存)。
主内存:真正的内存。
上述过程中,Java单独起了个名字 :JMM(Java Memory Model)
volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性, volatile 保证的是内存可见性.
volatile 解决的是一个线程读,一个线程写的问题(禁止指令重排序)
synchronized 解决的是两个线程写的问题
相关文章
- Unity 使用this关键字进行函数拓展 - Transform
- 并发编程(一)—— volatile关键字和 atomic包
- C语言中volatile关键字的作用
- Java并发编程:volatile关键字解析
- Java基础(4)-Java标识符和关键字
- volatile 关键字了解与使用
- oracle关键字作为字段名使用方法
- MySQL8.0数据库出现的问题——外码创建方式、外键约束两个引用列不兼容问题、check约束问题、用触发器代替check约束、关键字DELIMITER、删除添加索引、删除添加外键约束、和一些数据库方面的操作
- [C++] const和mutable关键字使用方法
- [实验]-从汇编代码来看volatile关键字的作用
- java中volatile关键字的含义
- 初识C语言(3):操作符和关键字
- LINUX小技巧,如何在指定目录下搜索到含特定关键字的文件。
- Java volatile关键字的作用