老王:小陈啊,上一章节结束之前让你看一下volatile,现在研究得怎么样了?
小陈:上一章结束之后啊,我回去看了一下资料,大概知道volatile是个啥东西了。
老王:哦,那你来说说。
小陈:volatile是java语言提供的一个关键字,用来修饰变量的,使用volatile修饰的变量可以保证并发安全的可见性和有序性。
小陈:volatile关键字的用法大概是下面这样的:
volatile int i = 0;
public void add() {
i++;
}
使用方法就是声明变量之前加一个volatile关键字,然后变量 i 的操作就跟我们平常的操作是一样的。
但是添加的volatile的变量,在编译之后JVM会在操作该变量的前后添加一些指令来保证可见性和有序性,具体添加了啥指令这个到时候还得靠老王你来讲解一下哈,因为我还没了解到那一步,哈哈。
老王:哈哈,没关系,到时候我再补充一下,你先来先说说volatile怎么保证可见性的?
小陈:结合之前讲的MESI缓存一致性协议,看了一些资料。大概知道volatile在JMM层次和CPU高速缓存层次是怎么确保可见性的了
老王:哦,这么机灵,那你来说说看....
小陈:使用volatile关键字修饰的共享变量,每次线程使用之前都会重新从主内存中重新读取最新的值;一旦该共享变量的值被修改了,修改它的线程比如立刻将修改后的值强制刷新回主内存。
小陈:我画个图来说一下:
(1)首先看一下上面的图,有工作线程A、工作线程B;假如之前工作线程A、B都是用过这个共享变量 i,工作内存中都有变量副本 i = 0
(2)这个时候工作线程A要执行 i++ 操作,按照volatile关键字的特性,每次使用之前必须从主内存重新读取,所以工作线程A重新从主内存读取(执行read、load指令)得到 i = 0没有变化
(3)然后执行use指令将 i = 0 传递给工作线程,执行 i++ 操作,得到 i = 1
(4)然后执行assign指令,将 i = 1的结果赋值给工作线程,按照volatile的特性;一旦共享变量的值被修改了,需要立即强制刷新回主内存。所以在执行assign赋值更新后的之后,立马执行store、write指令将最新的值传递到主内存,并且赋值给主内存的变量。
(5)此时工作线程B需要用到共享变量 i 了,即使工作内存里面有副本,但是每次还是会重新从主内存中读取最新的值,这个时候读取到 i = 1了
小陈:上面就是我知道的,volatile在java内存模型层次是怎么保证可见性的。
小陈:之前我们了解过MESI一致性怎么实现可见性了,由于java内存模型建立在CPU多级缓存模型之上,所以java内存模型底层也是通过MESI一致性的协议去达到可见性的目的,java内存模型会去适配不同操作系统对MESI一致性协议的不同实现方式。
老王:很好啊小陈,非常不错,棒棒的......
小陈:还好啦..,嘿嘿......
小陈:老王啊,我对volatile的了解就到这里了,后面其实我还有挺多疑问的,volatile修饰的变量,怎么让每都让它从主存读取最新数据的?修改了之后立刻刷新会主内存这个是怎么实现的?
还有volatile我看了一些资料说是通过禁止重排序来实现有序性的,那到底是通过什么来禁止重排序的?
老王:其实这个就涉及到一个内存屏障的概念了,其实volatile的可见性和有序性都是通过内存屏障来实现的。
包括你上面说的读取数据的时候强制读取主内存数据,修改数据之后强制刷新到主内存,都是有相对应的内存屏障指令对应的。
还有为了实现有序性而禁止volatile前后相关的指令进行重排序,在JVM乃至操作系统都是有相应的内存屏障指令的。
至于具体什么是内存屏障?有什么作用?我们下面的篇章再慢慢聊,今天就先到这里,我们下一章再见......
小陈:好的,我们下一章见。
1.什么是CPU多级缓存模型?
2.什么是JAVA内存模型?
3.线程安全之可见性、有序性、原子性是什么?
4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?
5.volatile怎么保证可见性?
6.什么是内存屏障?具有什么作用?
7.volatile怎么通过内存屏障保证可见性和有序性?
8.volatile为啥不能保证原子性?
9.synchronized是个啥东西?应该怎么使用?
10.synchronized底层之monitor、对象头、Mark Word?
11.synchronized底层是怎么通过monitor进行加锁的?
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
13.synchronized怎么保证可见性、有序性、原子性?
14. JDK底层Unsafe类是个啥东西?
15.unsafe类的CAS是怎么保证原子性的?
16.Atomic原子类体系讲解
17.AtomicInteger、AtomicBoolean的底层原理
18.AtomicReference、AtomicStampReference底层原理
19.Atomic中的LongAdder底层原理之分段锁机制
20.Atmoic系列Strimped64分段锁底层实现源码剖析
21.AQS是个啥?为啥说它是JAVA并发工具基础框架?
22.基于AQS的互斥锁底层源码深度剖析
23.基于AQS的共享锁底层源码深度剖析
24.ReentrantLock是怎么基于AQS实现独占锁的?
25.ReentrantLock的Condition机制底层源码剖析
26.CountDownLatch 门栓底层源码和实现机制深度剖析
27.CyclicBarrier 栅栏底层源码和实现机制深度剖析
28.Semaphore 信号量底层源码和实现机深度剖析
29.ReentrantReadWriteLock 读写锁怎么表示?
30. ReentrantReadWriteLock 读写锁底层源码和机制深度剖析
31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?
32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?
33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?
34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?
35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?
36.DelayQueue 底层源码剖析,延时队列怎么实现?
37.SynchronousQueue底层原理解析
38. 什么是线程池?看看JDK提供了哪些默认的线程池?底层竟然都是基于ThreadPoolExecutor的?
39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?
40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?
41. ThreadPoolExecutor execute执行流程?怎么进行任务提交的?addWorker方法干了啥?什么是workder?
42. ThreadPoolExecutor execute执行流程?何时将任务提交到阻塞队列? 阻塞队列满会发生什么?
43. ThreadPoolExecutor 中的Worker是如何执行提交到线程池的任务的?多余Worker怎么在超出空闲时间后被干掉的?
44. ThreadPoolExecutor shutdown、shutdownNow内部核心流程
45. 再回头看看为啥不推荐Executors提供几种线程池?
46. ThreadPoolExecutor线程池篇总结