并发编程----JMM模型,volatile,sychronized
JMM(java 内存 模型)
JMM(java 内存模型)是一种规范,解决线程中数据不一致问题(可见性,原子性,一致性),主要解决缓存一致性问题。
JMM模型图示:
JMM抽象模型
多线程情况JMM模型中会产生 原子性问题,可见性问题。
有序性问题是由 1、编译器指令重拍,2、处理器指令重排 3内存系统的重排序产生的。
JMM如何解决原子性,可见性,有序性问题
采用限制处理器的优化和使用内存屏障
原子性解决方案:sychronized(monitorenter/monitorexit)
可见性解决方案:volatile、sychronized、final
有序性解决方案:volatile、sychronized
volatile关键字
轻量级的锁,通过#lock解决可见性问题
内存屏障解决重排序的问题
从cpu层面了解内存屏障
内存屏障的作用:防止指令重排,保证数据的可见性
cpu屏障类型:store barrier(写屏障)、load barrier(读屏障)、full barrier(全屏障)
store barrier (写屏障 storestore barrier)
强制在storestore内存屏障之前的所有指令先执行,发送缓存失效的信号。
所有在storestore内存屏障的指令之后的store指令,必须在storestore内存屏障之前的指令执行完之后再执行
load barrier 读屏障 loadloadbarrier
强制在loadload内存屏障之前的所有指令先执行,
所有在loadload内存屏障的指令之后的load指令,必须在loadload内存屏障之前的指令执行完之后再执行
full barrier 全屏障 store和load两者的结合就是fullbarrier
内存屏障解决的是顺序一致性问题,并不能解决缓存一致性问题,缓存一致性问题是由缓存锁即MESI完成的
编译器(JMM)层面如何解决指令重排问题?
通过volatile 关键字可以去取消编译器上的缓存和重排序。防止重排序导致可见性问题,保证编译器的优化不会影响到代码实际执行顺序。 源码中有ACC_VOLATITLE
编译器层面将内存屏障分为四类
loadloadbarrier (例如 load1 loadload load2 表示load1的装载一定在load2之前 其他的内存屏障同理 )
storestorebarrier
loadstorebarrier
storeloadbarrier
内存屏障的功能:
1、确保指令重排序不会把内存屏障后面的指令排到内存屏障前面去,也不会把内存屏障前面的指令排到内存屏障后面去
2、强制对缓存的修改,会立即写到主内存里。
3、写操作会导致其他cpu的缓存无效
volatile的原子性
volatile没有办法保证符合(如 i++)操作的原子性,可以保证单操作的原子性(如 stop = flase)
i++ 实际上是三个操作
多个线程同时执行
首先getFiled load 然后i.add asgin 然后再去 putField store
storea storeb storeload loada loadb 能保证 storea 与 loada的顺序 但是无法保证 storeb 插入执行的顺序
在store 之后才会让其他可见,在store之前 没有办法改变
volatile的使用场景
使用场景:->线程的关闭
不加volatile 子线程无法结束,主线程改了stop的值,但是子线程没法得到stop为true,导致子线程无法结束
加了volatile,子线程可以结束