cpu会先从缓存中的缓存行读取内存值(不是内存地址),如果缓存没有命中,那么就从主内存中获取值,并且将获取到的值写入缓存行
volatile修饰过的变量转换成汇编语言会有个lock指令
lock指令会引发下面的情况:
(1)、锁定当前的缓存行,将当前cpu缓存行的数据回写到主内存
(2)、这个回写主内存的操作会使得其他cpu缓存了该内存的地址的缓存行无效,迫使cpu下次来缓存读取时无法命中,进而去读取主内存数据,达到缓存一致性的协议
每个线程都有自己的本地内存(是JMM的抽象概念,不是真实存在),本地内存存储了对应线程的共享变量的副本(共享变量主体再主内存),如果线程A、B之间需要进行通信时,当线程A修改了它的本地内存的共享变量,就会将变量刷新回主内存,线程B就必须去主内存读线程A修改过后的值,并且存在自己的本地内存中
线程A对有volatile修饰的变量C进行写操作时,JMM会把对应本地内存的共享变量刷新回主内存,并且线程B想要读取变量C,JMM会将它得本地内存包含C的值置为无效,强制线程B去读主内存中C的值
分为3块:java编译器重排序、指令级重排序(cpu级别)、内存系统重排序(cpu级别),可以通过volatile修饰符在读操作或者写操作的前后加入内存屏障
规则:
(1)、当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序,而且会将第二个操作之前的普通写操作回写主内存,达到前面的普通写操作都对别的线程可见
(2)、当第一个volatile是读操作时,不管第二个操作是什么,都不能重排序
(3)、当第一个volatile操作是写,第二个volatile操作是读是,2着都不能重排序
既然volatile能够在A线程的写操作让别的线程的缓存行无效,如果别的线程要读缓存行就必须从主内存读,但是如果在高并发下的读写操作只用volatile修饰变量会导致线程A刚针对这个变量进行修改并且将别的线程的缓存行置为无效,但是线程B已经拿到这个变量的值了,但是这个变量的值是无效的,所以,volatile是不能保证数据读写的安全性,一旦涉及到这个就必须通过synchronized修饰