小陈:老王,看了上一篇的《CPU多级缓存模型》,有个疑问为什么还要有JAVA内存模型啊?
老王:这么来说吧,CPU多级缓存模型,只是一个规范,但是底层基于这个规范的实现还是有很多种的。比如:
(1)基于这个模型之上不同的计算机厂商可能对这个模型底层的实现不一样,包括底层运行的指令集、有的还基于多级缓存模型之上还引入了写换从器、无效队列等。
(2)不同的操作系统windows、linux的指令集不一样。
老王:
所以JAVA语言为了应对上述的问题:不同的系统、不同的计算机厂商的底层实现不同。基于它们之上设计了一个JAVA内存模型(JMM)的规范,就是为了屏蔽底层操作系统、厂商之间的差异性。程序员运行是只需要关注JMM,而不需关注底层系统、厂商指令的差异性,这些由JMM去适配不同的系统和厂商的指令集,这样就能实现一次编写,“到处乱跑”的跨平台效果。
小陈:哦哦,原来是为了屏蔽操作系统、不同的计算机厂商的底层的差异性,实现跨平台的无差异性运行的效果啊。
小陈:原因我大概知道了,那JAVA内存模型大概是个什么样的结构啊?
老王:给你看下下面这张图,你大概就理解了
老王:上图就是JAVA内存模型的大致结构图,JAVA内存模型定义了一个规范。那就是每个线程都有自己的工作内存,线程操作共享变量的时候需要从主内存读取到自己的工作内存,然后在传递给工作线程使用,共享变量修改后先刷新到工作内存,然后再刷新回主内存;这个JAVA内存模型是基于我们上一讲CPU多级缓存模型上建立的。
小陈:看了上面的图,JAVA内存模型的结构我基本知道了,但是JMM这哥们怎么工作的我还是很模糊啊?
老王:没关系,我来给你讲讲,JMM定义了8种指令,它工作的时候就是通过这8种指令来操作内存的。
包括怎么锁定变量、怎么将数据从主存传到工作内存、怎么传给正在被CPU调度的线程、修改之后CPU怎么传回工作内存、工作内存又怎么传递回主存:
小陈:...., 这都啥啊...
老王:给你画张图吧,你就了解了:
比如线程A要执行一个x++的操作,可能要经历下面的过程:
小陈:牛逼啊老王....
小陈:额,感觉不对啊,上面只有六种指令,还有另外两种lock、unlock指令是咋用的?
老王:这还不简单,再给你画张图就KO了。
比如还是上面的x++操作
(1)工作线程A操作该共享内存变量的时候,执行lock指令,主内存中的这个共享变量;同时告诉线程B这个共享变量我准备修改了,让它失效掉。
(2)B线程的变量副本失效之后,运行时候用到,需要到主内存重新读取(执行read、load操作放入工作内存);发现该主内存的变量被锁定了,读取失败;此时相当于线程A拥有该变量的独享操作
(3)线程A执行i++操作,经历上述说的(read、load、use、assign、store、write)指令之后;操作完成执行unlock释放锁定的这个内存变量
(4)线程B这个时候再去主内存读取的时候,发现未被锁定,就可以重新读取了
小陈:牛逼啊老王,听君一席话,白读十年书。献上我的掌声......
小陈:JMM以及它的8中基本操作我了解了,但是我发现了个问题,在多线程操作的时候可能会有数据不一致的问题,比如我画个图讲一下我的想法:
线程A和线程B同时执行共享变量x的++操作;线程B在线程A将x=1的值刷入主内存之前读取x=0,这样就会导致数据错了。
老王:很好,看来小陈很聪明啊,理解得很快啊
小陈:嘿嘿,一般般啦...
老王:你说的这个问题,正是多线程并发操作的可见性问题。
小陈:我知道多线程并发操作的时候,会有可见性、有序性、原子性的问题,这种情况会导致可见性问题我知道了,但是有序性和原子性是什么?什么时候会发生的?会导致什么问题?
老王:哈哈,你有这个好奇心很好,我们今天先说这么多,可见性、有序性、原子性的问题我们留到下一篇再说,别急哦,一点点慢慢来,啧啧..
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怎么保证可见性、有序性、原子性?
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 读写锁怎么表示?
31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?
32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?
33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?
34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?
35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?
36.DelayQueue 底层源码剖析,延时队列怎么实现?
37.SynchronousQueue底层原理解析
39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?
40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?