保证了内存可见性
JMM
Java内存模型,主要指的是主内存和工作内存
1、多个线程共享的变量,首先存储在主内存中,各个线程都有自己的工作内存,从主内存中将该变量值读取(拷贝)到自己的工作内存中,线程访问自己的工作内存,不能访问其他线程的工作内存
2、线程操作自己工作内存中的这些共享变量副本,操作完成之后同步到主内存中
3、一个线程修改自己工作内存中的变量,对其他线程不可见,会导致线程不安全的问题,所以JMM规定了8种原子操作,用来控制内存之间的交互
lock:锁定,作用于主内存的变量,把一个变量标识为线程独占状态
unlock:解锁,作用于主内存的变量,把一个处于锁定状态的变量释放出来,其他线程才能有机会访问这个变量
read:读取,作用于主内存的变量,把一个变量的值从主内存读取到工作内存种
load:载入,作用于工作内存的变量,把read读取的值保存到工作内存的变量副本中
use:使用,作用于工作内存的变量,把工作内存中的变量传给执行引擎
assign:赋值,作用于工作内存的变量,将执行引擎处理返回的值重新赋给工作内存中的变量副本
store:存储,作用于主内存的变量,将变量副本的值存储到主内存中
write:写入,作用于主内存的变量,将store存储的值写入到主内存的共享变量中
所以,一个线程 操作自己工作内存中的共享变量副本,操作完成之后同步到主内存中,主内存在将最新的共享变量值同步给其他线程之前,其他线程的工作内存中的变量还是之前的旧值,这就可能导致线程不安全
什么是可见性
JAVA为了提高访问效率,每个线程都有自己的工作内存(类似缓存),各个线程间是相互独立的,访问优先访问线程自己的工作内存,工作内存中没有再去主内存中找
这样的话,很可能导致一个线程t1修改自己工作内存中的变量后(由1改成2),但是主内存中的这个变量值还是1;另一个线程t2的工作内存中的这个变量还是初始的1(工作内存之间相互独立的),然后t2修改这个变量(由1改成2),原本两个线程操作完成后这个变量值应该是3才对的,现在是2,导致这个问题出现的原因就是没有保证变量之间的可见性
如何保证可见性
volatile修饰的变量可以保证可见性,就不会出现上面的问题了
(1)lock指令

可以发现在修改volatile修饰的变量时,JVM会向处理器发送一条lock前缀的指令,把主内存中的这个变量标记为线程独占,并且将最新的值刷回主内存中,其他线程的工作内存中的这个变量副本失效
(2)缓存一致性协议
当cpu写数据时,如果操作的变量是共享变量,即其他cpu中也存在该变量副本,会发信号通知其他cpu将该变量的缓存行置为无效状态,其他cpu需要读取这个变量时,发现缓存中缓存该变量的缓存行是无效的,就会从内存中重新读取,备注内部缓存、系统内存和其他处理器缓存数据的一致性
通过这两个机制,确保了一个volatile修饰的共享变量,在数据发生改变后,变量值立即刷入主存;其他处理器中的缓存由于遵守缓存一致性协议,也会把自己工作内存中缓存该变量的缓存行置为无效,保证在使用该共享变量时去主内存中重新读取放到自己的工作内存中,进而保证了内存的可见性
