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

已知:

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

问题:

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

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

java中的一行行代码,对应到硬件层面,就是一个个指令,现代处理器为了加快编译速度,有可能会乱序执行指令,这样也就无法保证代码的有序性了。
java进阶(6)之从硬件底层剖析synchronized/volatile原理_第1张图片

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

图解CPU缓存模型

简化版CPU缓存模型请移步:java进阶(4)之volatile关键字深入详解
java进阶(6)之从硬件底层剖析synchronized/volatile原理_第2张图片
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机制保障原子性

java进阶(6)之从硬件底层剖析synchronized/volatile原理_第3张图片
synchronized关键字修饰的代码块或者方法,会被指令monitorentermonitorexit包裹,包裹的模块可以称之为ObjectMonitor,当有线程进入时,会执行CAS加锁,从而保证了原子性。

你可能感兴趣的:(java进阶)