JMM

JMM java memary model java内存模型)

一、CPU架构缓存一致性

 

 

 

现在计算机都加高级缓存,让运算速度加快,在运算过程中把某些变量放到高级缓存里面,等运算结束以后,再从高级缓存同步到内存中,这样避免IO读写过慢CPU的空闲。

 

加入高级缓存会带来问题,带来缓存一致性的问题,在多线程情况下,看不见已经修改的变量的值。最初的解决方案就是总线锁的方式,总线锁性能比较低,多个CPU出现竞争,有的cpu出现空闲等待。

 

现在的cpu是基于缓存一致性协议实现的内存模型。最出名的缓存一致性协议internet mesi,共享变量a在处理器都存在副本,如果变量被某一个处理器改了会发出信号告诉其他的CPU置成无效的。

 

1、缓存一致性协议

 

缓存一致性协议给缓存行(通常为64字节)定义了个状态,用来描述该缓存行是否被多处理器共享、是 否修改,缓存一致性协议最出名的是mesi。

 

独占:仅当前处理器拥有改缓存行

 

共享:多个处理器拥有该缓存行,每个处理器都没修改过缓存值。

 

修改:仅当前处理器拥有改缓存行,并且缓存行被修改过。

 

失效:缓存行被其它处理器修改过,不是最新的,就会变成失效状态,重新读取值。

 

二、CPU相关术语

 

内存屏障:Memory barriers 是一组处理器指令,用于实现对内存操作的顺序执行。

 

缓存行:Cache line 缓存中可以分配的最小存储单位

 

三、原子性、可见性、有序性

 

1、原子性:readloaduseassignstorewritelockunlock 8个汇编指令。Monitorenter monitorexit。线程之间互不影响,不被中断。

 

2、可见性:一个线程操作的变量对其它线程可见

 

3、有序性:线程内都是有序的,线程内观察别的线程都是无序的。

 

四、JMM模型

 

1、工作内存中,线程A改了对线程B不可见。

 

 

 

 

2、JMM内存模型分析

Read 从主内存里读出来,load载入,作用于工作内存,从内存读的值放到工作变量里。User 作用于工作内存,把变量传递给执行引擎(cpu)。Assign 赋值,主要作用于工作内存。Store存储,主要作用于工作内存。Write 主要作用于主内存。

3、指令重排

 

 

编译器重排序:不改变单线程执行结果的前提,对语句执行顺序进行优化。

指令级并行重排序:采用并行技术ilp,多条指令的重叠执行。

内存系统得重排序:处理器使用缓存和读写缓存序列,使我们的加载存储操作是乱序执行的,as if serial单线程语义规则。

4、Happens-before

只是一个概念

5、内存屏障

1)阻止屏障两侧的指令重排序

2)强制把写缓存去/高速缓存区的脏数据写回主内存,让缓存中响应的数据失效。

JSR规范中定义了4种内存屏障:

• LoadLoad屏障:(指令Load1; LoadLoad; Load2),在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

• LoadStore屏障:(指令Load1; LoadStore; Store2),在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕

• StoreStore屏障:(指令Store1; StoreStore; Store2),在Store2及后续写入操作执行前,保证Store1 的写入操作对其它处理器可见

• StoreLoad屏障(比较常用):(指令Store1; StoreLoad; Load2),在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

Jdk源码地址

https://github.com/openjdk/jdk

看汇编时需要下载一个软件 hisdis 下载dll 放在jre下面的lib下面,下载jclasslib bytecode Viewer插件,通过这个插件可以看class的字节码。下载JITWatch 看汇编

 

 

6、Volatile

用StoreLoad 内存屏障,防止指令重排序。

 

 

五、可见性

1、加了volatile,有lock就总线嗅探,如果发生变化,使自己的变量失效,重新加载

 

 

2、单例的双重检索,这样写的好处是,延迟初始化来降低初始化类和创建对象的一个开销。

 

 

使用单例模式,如果没有加volatile,会有指令重排序,导致拿到的instance为空,而报错。

 

 

 

六、CAS

独占锁是一种悲观锁,synchronized就是一种悲观锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放。

乐观锁是一种更加有效的锁,每次不加锁,而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

 

 

cmpxchg是汇编指令
作用:比较并交换操作数

2、问题:以下counter的输出小于20万,并且一般情况下小于20万。Counter的操作不具有原子性。

 

 

AtomicInteger 共享变量包装类。使变量变为原子性。

While自旋,直到成功为止。

 

 

 

 

 

 

 

posted @ 2021-04-02 13:38  majingyun  阅读(297)  评论(0编辑  收藏  举报