JMM(Java内存模型)

220 阅读3分钟

多线程一致性的硬件层支持

cpu计算数据流程

硬盘 -> 内存(缓存) -> cpu(缓存)

cpu内的缓存可能导致数据不一致问题

解决方案:

1、bus(总线锁) 老的cpu用,就是在CPU和内存交互都通过bus,bus中有锁

2、MESI(intel CPU)用的MESI协议:MESI协议是对数据标记的几种状态

还有很多协议,不同的CPU用不同的协议

现在CPU的数据一致性实现 = 缓存锁(MESI) + 总线锁(有些数据太大没有缓存)

伪共享

由于CPU内缓存是以**缓存行(64个字节)**为单位的,可能缓存行其他数据用不到,但是也在缓存行里,频繁的刷新缓存行

伪共享的解决

通过使用缓存行的对齐提高效率,

例如:把一个long 变量 前后都放七个long类型,long占8个字节,所以不管在哪,这个变量都自占一个缓存行,但是浪费内存,自己衡量。

乱序问题

乱序行程的原因:

几个有序执行让CPU执行,当CPU执行第一个时,可能去内存查这个数据。CPU的速度是内存的上百倍,如果等到内存返回结果再去执行,那么CPU效率也会降低,所以CPU进行了优化,后续的执行命令如果不依靠第一个命令,换句话说,没有执行顺序的必然联系,那么cpu不等第一个执行完,就回去执行接下来的命令。所以导致乱序

CPU有合并写机制,占有四个字节,比每次写6个字节要快

如果保障有序性

volatile有序

硬件保证有序性

  • intel CPU级别的内存屏障,读屏障(lfence )、写屏障(sfence)、读写屏障(mfence)
  • Lock 指令也可以

JVM级别如果规范

jvm只是规范,具体的实现依靠硬件

LoadLoad屏障: 对于这样的语句Load1; LoadLoad; Load2,

在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:

对于这样的语句Store1; StoreStore; Store2,

在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

LoadStore屏障:

对于这样的语句Load1; LoadStore; Store2,

在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障: 对于这样的语句Store1; StoreLoad; Load2,

在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

volatile的实现

1、字节码层面:在字节码层面就加了一个 ACC_VOLATILE的标识

2、JVM层面:在volatile内存区的读写 都加屏障

3、OS(操作系统)和硬件层面:不同的硬件不同的实现方式,Windows 用lock指令实现的

synchronize的实现

1、字节码层面:在字节码层面 monitorenter monitorexit

2、JVM层面:C C++调用了操作系统提供的同步机制

3、OS(操作系统)和硬件层面:X86,用lock指令实现的