什么是内存屏障?
小陈:老王,上一篇你引出了volatile底层是通过内存屏障来解决可见性和有序性问题的。首先我想问一下什么是内存屏障?
老王:内存屏障啊,本质上也是一种指令,只不过它具有屏障的作用而已。
小陈:额,这怎么说...
老王:首先内存屏障是一种指令,无论是在JAVA内存模型还是CPU层次,都是有具体的指令对应的,是一种特殊的指令。
小陈:嗯嗯,它是一种特殊的指令。还是不明白......
小陈:然后呢?
老王:然后这种指令具有屏障的作用,所谓屏障,也就是类似关卡,类似栅栏,具有隔离的作用。
小陈:那它是怎么实现隔离作用的,能否搞个例子讲一下。
老王:哈哈,这个举例没问题,我先跟你说说内存屏障的分类,后面再给你实例讲一下:
老王:按照内存屏障的分类,我理解有两类。
(1)一类是强制读取主内存,强制刷新主内存的内存屏障,叫做Load屏障和Store屏障
(2)另外一类是禁止指令重排序的内存屏障,有四个分别叫做LoadLoad屏障、StoreStore屏障、LoadStore屏障、StoreLoad屏障
老王:下面再给你介绍一下这两类内存屏障各自的作用:
强制读取/刷新主内存的屏障
Load屏障:执行读取数据的时候,强制每次都从主内存读取最新的值。
Store屏障:每次执行修改数据的时候,强制刷新回主内存。
先给你画图讲解一下Load屏障:
如上图所示:在工作内存的变量名、变量的值之前有一道关卡或者栅栏,导致变量 i 获取不到工作内存中的值,所以每次只好主内存重新加载咯。
然后再给讲一下Store屏障:
如上图所示,每次执行assign指令将数据变更之后,后面都会紧紧跟着一个Store屏障,让你立刻刷新到主内存。
老王:小陈,我画这两图讲强制读取和刷新主内存的屏障(Load屏障和Store屏障),你看懂了嘛?
小陈:也就是说,只要加了Load屏障,相当于加了一个栅栏,不管工作内存是否有数据,都是从主内存读取数据。只要加了Store屏障,具有强制作用,进行assign操作将变量更改了之后,立刻将变量刷新到主内存里面是吗?
老王:是的,就是这么一个道理,上面的图只是为了方便你理解画出来的,实现上并不一定完全跟图的一样,但是原理差不多。
小陈:嘿嘿,老王真棒......
禁止指令重排序的屏障
老王:好了,下面再给你讲讲另外一类的内存屏障,下面这类的内存屏障的作用是禁止指令重排序,JAVA内存模型层次关于禁止重排序有下面4种屏障:
LoadLoad屏障
序列:load1指令 LoadLoad屏障 load2指令
作用:在load1指令和load2指令之间加上 LoadLoad屏障,强制先执行load1指令再执行load2指令;load1指令和load2指令不能进行重排序(LoadLoad屏障 前面load指令禁止和屏障后面的load指令进行重排序)。
StoreStore屏障
序列:store1指令 StoreStore屏障 store2指令
作用:在store1指令和store2指令之间加上StoreStore屏障,强制先执行store1指令再执行store2指令;store1指令不能和store2指令进行重排序(StoreStore屏障 前面的store指令禁止和屏障后面的store指令进行重排序)
LoadStore屏障
序列:load1指令 LoadStore屏障 store2指令
作用:在load1指令和store2指令之前加上LoadStore屏障,强制先执行load1指令再执行store2指令;load1指令和store2执行不能重排序(LoadStore屏障 前面的load执行禁止和屏障后面的store指令进行重排序)
StoreLoad屏障
序列:store1指令 StoreLoad屏障 load2指令
作用:在store1指令和load2指令之间加上StoreLoad屏障,强制先执行store1指令再执行load2指令;
store1指令和load2指令执行不能重排序(StoreLoad屏障 前面的Store指令禁止和屏障后面的Store/Load指令进行重排)
老王:小陈,我上面说的这几种内存屏障,你能理解吗?
小陈:感觉听着有点玄乎啊,实际上是怎么禁止重排序的啊...
老王:我下面给你画个图,以StoreStore屏障和StoreLoad屏障举个例子,你就知道大概是什么意思了:
(1)有三个区域分别是区域1、区域2、区域3
(2)区域1和区域2加了 StoreStore屏障,这样区域1和区域2的Store指令就被隔离开来,不能重排了
(3)区域2和区域3加了StoreLoad屏障,这样区域2和区域3的Store指令、Load指令就被隔离开来,不能重排了
(4)就相当于搞了个栅栏,禁止各个区域之间的指令跳来跳去的,否则就会导致乱序执行
小陈:哎呀,原来是搞了个栅栏啊,说白了就是相当于搞了个围墙,不让各个指令之间跳来跳去的,这样达到禁止区域之间重排序的效果。
小陈:牛啊,老王;看起来这么深奥的道理,竟被你搞个图这么简单的说清楚了,献上我的膝盖...
老王:嘿嘿,还好还好......
小陈:内存屏障的底层是怎么保证可见性和有序性的,通过这次讨论我明白了。但是volatile是怎么使用内存屏障的我还不明白啊?
老王:别急啊,小陈。慢慢来,今天我们先讲到内存屏障这里,volatile怎么使用内存屏障的?我们下一章再说一下。
其实不只是volatile使用到了内存屏障,还有像是synchronized关键字底层也是用到内存屏障的。
今天我们先说到这里,下一篇《volatile怎么通过内存屏障保证可见性和有序性? 》我们来继续讨论
小陈:好的,老王,我们下一章见。
目录
JAVA并发专题 《筑基篇》
4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?
JAVA并发专题《练气篇》
6.什么是内存屏障?具有什么作用?
7.volatile怎么通过内存屏障保证可见性和有序性?
8.volatile为啥不能保证原子性?
9.synchronized是个啥东西?应该怎么使用?
10.synchronized底层之monitor、对象头、Mark Word?
11.synchronized底层是怎么通过monitor进行加锁的?
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
13.synchronized怎么保证可见性、有序性、原子性?
JAVA并发专题《结丹篇》
- JDK底层Unsafe类是个啥东西?
15.unsafe类的CAS是怎么保证原子性的?
16.Atomic原子类体系讲解
17.AtomicInteger、AtomicBoolean的底层原理
18.AtomicReference、AtomicStampReference底层原理
19.Atomic中的LongAdder底层原理之分段锁机制
20.Atmoic系列Strimped64分段锁底层实现源码剖析
JAVA并发专题《金丹篇》
21.AQS是个啥?为啥说它是JAVA并发工具基础框架?
22.基于AQS的互斥锁底层源码深度剖析
23.基于AQS的共享锁底层源码深度剖析
24.ReentrantLock是怎么基于AQS实现独占锁的?
25.ReentrantLock的Condition机制底层源码剖析
26.CountDownLatch 门栓底层源码和实现机制深度剖析
27.CyclicBarrier 栅栏底层源码和实现机制深度剖析
28.Semaphore 信号量底层源码和实现机深度剖析
29.ReentrantReadWriteLock 读写锁怎么表示?
- ReentrantReadWriteLock 读写锁底层源码和机制深度剖析
JAVA并发专题《元神篇》并发数据结构篇
31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?
32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?
33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?
34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?
35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?
36.DelayQueue 底层源码剖析,延时队列怎么实现?
37.SynchronousQueue底层原理解析
JAVA并发专题《飞升篇》线程池底层深度剖析
- 什么是线程池?看看JDK提供了哪些默认的线程池?底层竟然都是基于ThreadPoolExecutor的?
39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?
40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?
-
ThreadPoolExecutor execute执行流程?怎么进行任务提交的?addWorker方法干了啥?什么是workder?
-
ThreadPoolExecutor execute执行流程?何时将任务提交到阻塞队列? 阻塞队列满会发生什么?
-
ThreadPoolExecutor 中的Worker是如何执行提交到线程池的任务的?多余Worker怎么在超出空闲时间后被干掉的?
-
ThreadPoolExecutor shutdown、shutdownNow内部核心流程
-
再回头看看为啥不推荐Executors提供几种线程池?
-
ThreadPoolExecutor线程池篇总结