JMM 为 volatile 定义了一些特殊访问规则,当变量被定义为 volatile 后具备两种特性:
保证变量对所有线程可见
当一条线程修改了变量值,新值对于其他线程来说是立即可以得知的。
volatile 变量在各个线程的工作内存中不存在一致性问题,但 Java 的运算操作符并非原子操作,导致 volatile 变量运算在并发下仍不安全。
禁止指令重排序优化
使用 volatile 变量进行写操作,汇编指令带有 lock 前缀,相当于一个内存屏障,后面的指令不能重排到内存屏障之前。
使用 lock 前缀引发两件事:
① 将当前处理器缓存行的数据写回系统内存。
②使其他处理器的缓存无效。相当于对缓存变量做了一次 store 和 write 操作,让 volatile 变量的修改对其他处理器立即可见。
静态变量 i 执行多线程 i++ 的不安全问题
自增语句由 4 条字节码指令构成的,依次为 getstatic、iconst_1、iadd、putstatic,当 getstatic 把 i 的值取到操作栈顶时,volatile 保证了 i 值在此刻正确,但在执行 iconst_1、iadd 时,其他线程可能已经改变了 i 值,操作栈顶的值就变成了过期数据,
所以 putstatic 执行后就可能把较小的 i 值同步回了主内存。
适用场景
① 运算结果并不依赖变量的当前值。② 一写多读,只有单一的线程修改变量值。
内存语义
写一个 volatile 变量时,把该线程工作内存中的值刷新到主内存。
读一个 volatile 变量时,把该线程工作内存值置为无效,从主内存读取。
指令重排序特点
第二个操作是 volatile 写,不管第一个操作是什么都不能重排序,确保写之前的操作不会被重排序到写之后。
第一个操作是 volatile 读,不管第二个操作是什么都不能重排序,确保读之后的操作不会被重排序到读之前。
第一个操作是 volatile 写,第二个操作是 volatile 读不能重排序。
JSR-133 增强 volatile 语义的原因
在旧的内存模型中,虽然不允许 volatile 变量间重排序,但允许 volatile 变量与普通变量重排序,可能导致内存不可见问题。
JSR-133 严格限制编译器和处理器对 volatile 变量与普通变量的重排序,确保 volatile 的写-读和锁的释放-获取具有相同的内存语义。
涨知识了
我想问一下前端面试的时候可以自选语言嘛?平常刷题都是用的java语言…
面试官逮着我问内存溢出和内存泄露,k8s,测试前置,jekins集群的问题