Java volatile(MESI)

发布于 2020-05-13  247 次阅读


volatile是基于缓存一致性协议的保证多线程下的有序性和可见性的关键字(修饰类成员变量和类静态成员变量),volatile在JMM的主内存和工作内存之间加了一个MESI(修改,锁定,无效,共享四种状态)

可见性:通过volatile修饰的成员变量在并发下,值被修改其它线程被通知把工作内存的值设为无效状态,重新读主内存的值并运算

有序性:volatile变量,禁止指令的重排,在JVM不经过优化,在程序的前后代码有序执行

可见性演示:

注:线程的可见性问题只有在高并发下的小概率事件,在cpu的高速读载中,线程读写快,并不会发生可见性问题(所有有些情况不加volatile也看起来没问题)

http://wql.luoqin.ltd/wp-content/uploads/2020/05/1.1.png

MESI:

MESI的四种操作:

1,修改:线程通知协议我要修改值

2,失效:线程发出修改指令,MESI将其他线程的值改为无效

3,锁定(排它性):在一个线程没有修改完成时,其他线程不允许操作

4,共享:线程修改完成后,MESI通知其他线程值一更新,其他线程读取新值

http://wql.luoqin.ltd/wp-content/uploads/2020/05/MESI.png

问题:MESI并不能保证原子性,在线程高并发下,一个线程在发出修改时,另一个线程已经将数据交给了执行引擎之后就有了两次共享,MESI不能阻止这一步操作(所有基于MESI的volatile也不能保证原子性)

http://wql.luoqin.ltd/wp-content/uploads/2020/05/MESI1.png

y的值不会到达100,无法保证原子性

2指令重排

指令重排是引发有序性问题的根本所在

指令重排是JVM的优化机制,为优化程序效率一些代码会被优先执行(程序计数器中),从而有些会错乱 ,所有一些要刻意避免重排

避免重排的方法:

1.加volatile关键字(原因:volatile基于MESI,有内存屏障问题,禁止重排)

2,加锁如synchronized锁和Lock锁(原因:jvm中单机模式下必须保证有序性,在锁中每个线程在执行都是是串行的,as-if-serial                                        语义把单线程程序保护了起来)

 

 

 


路漫漫其修远兮,吾将上下而求索