volatile
原因
最大化的利用 CPU 提升性能,从硬件、操作系 统、编译器等方面都做出了很多的优化
1. CPU 增加了高速缓存==== 可见性
2. 操作系统增加了进程、线程。通过 CPU 的时间片切换最 大化的提升 CPU 的使用率 ==== 原子性
3. 编译器的指令优化,更合理的去利用好 CPU 的高速缓存 === 有序性
可看https://blog.csdn.net/a_higher/article/details/109728467
解决方案
1.总线加锁(粒度太大)
2.MESI
a.读操作:不做任何事情,把Cache中的数据读到寄存器
b.写操作:发出信号通知其他的CPU讲改变量的Cache line置为无效,其他的CPU要访问这个变量的时候,只能从内存中获取
java内存模型
- 主存中的数据所有线程都可以访问(共享数据)
- 每个线程都有自己的工作空间,(本地内存)(私有数据)
- 工作空间数据:局部变量、内存的副本
- 线程不能直接修改内存中的数据,只能读到工作空间来修改,修改完成后刷新到内存
volatile作用
保证有序性和可见性
- 对共享变量的修改,其他的线程马上能感知,但不能保证原子性
实现原理
借助了CPU的lock指令。在生成汇编代码时会在volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令,在读写的时候插入内存屏障。从而使得:将当前处理器缓存行的数据写回系统内存;这个写回内存的操作会使得其他CPU 里缓存了该内存地址的数据无效,当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值。这样volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值
内存屏障
通过禁止指令重排序来实现,也就是加内存屏障,有的处理器这么处理,有的不是
- 在每个volatile写操作的后面插入一个StoreStore屏障,防止写volatile与后面的写操作重排序。
- 在每个volatile写操作的后面插入一个StoreLoad屏障,防止写volatile与后面的读操作重排序。
- 在每个volatile读操作的后面插入一个LoadLoad屏障,防止读volatile与后面的读操作重排序。
- 在每个volatile读操作的后面插入一个LoadStore屏障,防止读volatile与后面的写操作重排序。
volatile写-读的内存语义(实现mesi)
- 只有当线程T对变量V执行的前一个动作是load的时候,线程T才能对变量V执行use动作;并且,只有当线程T对变量V执行的后一个动作是use的时候,线程T才能对变量V执行load动作。线程T对变量V的use动作可以认为是和线程T对变量V的load、read动作相关联,必须连续一起出现(这条规则要求在工作内存中,每次使用V前都必须先从主内存刷新最新的值,用于保证能看见其他线程对变量V所做的修改后的值)。
- 只有当线程T对变量V执行的前一个动作是assign的时候,线程T才能对变量V执行store动作;并且,只有当线程T对变量V执行的后一个动作是store的时候,线程T才能对变量V执行assign动作。线程T对变量V的assign动作可以认为是和线程T对变量V的store、write动作相关联,必须连续一起出现(这条规则要求在工作内存中,每次修改V后都必须立刻同步回主内存中,用于保证其他线程可以看到自己对变量V所做的修改)。
这里的1,2参考https://blog.csdn.net/qq_33808244/article/details/100836342
意思如下:
- volatile写的内存语义:当写线程写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
- volatile读的内存语义:当读线程读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存读取共享变量。
volatile重排序语义(happens-before)
- 假定动作A是线程T对变量V实施的use或assign动作,假定动作F是和动作A相关联的load或store动作,假定动作P是和动作F相应的对变量V的read或write动作;类似的,假定动作B是线程T对变量W实施的use或assign动作,假定动作G是和动作B相关联的load或store动作,假定动作Q是和动作G相应的对变量W的read或write动作。如果A先于B,那么P先于Q(这条规则要求volatile修饰的变量不会被指令重排序优化,保证代码的执行顺序与程序的顺序相同)
使用场景
状态标志(开关)
DCL
happen-before
与synchronize的区别
使用上的区别
Volatile只能修饰变量,synchronized只能修饰方法和语句块
对原子性的保证
synchronized可以保证原子性,Volatile不能保证原子性
对可见性的保证
都可以保证可见性,但实现原理不同
Volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM
对有序性的保证
Volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行
其他
synchronized引起阻塞
Volatile不会引起阻塞