java进阶(6)之从硬件底层剖析synchronized/volatile原理

不羁的心 提交于 2020-03-02 11:06:02

已知:

  • java中的synchronized关键字能保证可见性,有序性,原子性;
  • volatile关键字能保证可见性,有序性。

问题:

  • 为什么java中的并发,在硬件层面不能保证,非要在JVM里处理呢?
  • 它们在硬件层面是如何对应保证的呢?
  • java层面为啥要加这两个关键字才能保证java的并发特性呢?

指令重排序无法保证有序性

java中的一行行代码,对应到硬件层面,就是一个个指令,现代处理器为了加快编译速度,有可能会乱序执行指令,这样也就无法保证代码的有序性了。
在这里插入图片描述

现代CPU缓存模型无法保证可见性和原子性

图解CPU缓存模型

简化版CPU缓存模型请移步:java进阶(4)之volatile关键字深入详解
在这里插入图片描述
1. 高速缓存中的tag表示数据所对应的内存地址,cacheline表示数据本身,flag表示数据状态(E表示exclusive写,S表示shared读)

2. Java层面的读写数据,对应到硬件层面,就是主内存/高速缓存的读写;

3. 处理器与处理器之间的数据交互,或者说处理器与主内存的数据交互,是通过总线的嗅探机制来实现的,也可以说是缓存一致性协议(我把嗅探机制类比成java层面的EventBus);

4. 现代CPU为了加快读写速度,加入了写缓冲器和无效队列,但是这样的话,数据只有读写完成了,才能进入到高速缓存/主内存,这样会导致数据无法及时更新,从而无法保证原子性,可见性。

内存屏障保障有序性和可见性

有序性保障

  • 每个volatile/synchronized写操作前面,加StoreStore屏障,禁止上面的普通写和volatile/synchronized重排;
  • 每个volatile/synchronized写操作后面,加StoreLoad屏障,禁止跟下面的volatile/synchronized读/写重排;
  • 每个volatile/synchronized读操作前面,加LoadLoad屏障,禁止上面的普通读和volatile/synchronized读重排;
  • 每个volatile/synchronized读操作后面,加LoadStore屏障,禁止下面的普通写和volatile/synchronized读重排。

可见性保障

  • volatile/synchronized关键字修饰的变量或者代码块在加载之前,会去硬件底层执行refresh指令,将高速缓存/主内存的最新数据刷新到本地工作内存。
  • volatile/synchronized关键字修饰的变量或者代码块在加载之后,会去硬件底层执行flush执行,将数据刷到高速缓存/主内存中。

synchronized中CAS机制保障原子性

在这里插入图片描述
synchronized关键字修饰的代码块或者方法,会被指令monitorentermonitorexit包裹,包裹的模块可以称之为ObjectMonitor,当有线程进入时,会执行CAS加锁,从而保证了原子性。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!