volatile为什么不能保证原子性
小陈:老王,快来快来,上一篇结尾说volatile不能保证原子性,我现在迫不及待了...
老王:哈哈,来了,马上开搞......
老王:按照惯例,我还是先来给你画张图:
还是以 i 的那个例子为例,volatile int i = 0,假如两个线程A、线程B同时对 i 进行 操作如下:
上图存在一种情况就是,线程A、线程B如果几乎同时读取 i = 0 到自己的工作内存中。
线程A执行 i 结果后将 i = 1 赋值给工作内存;但是这个时候还没来的将最新的结果刷新回主内存的时候,线程B就读取主内存的旧值 i = 0 ,然后执行use指令将 i = 0的值传递给线程B去进行操作了。
即使这个时候线程A立即将 i = 1刷入主内存,那也晚了;线程B已经使用旧值 i = 0进行操作了,像这种情况计算结果就不对了。
老王:小陈,我上面的那个图讲解,你可以听懂嘛?
小陈:嗯嗯,看图解释就是,线程A的 i 结果, 也就是 i = 1还没刷回主内存;线程B就执行 use指令将 i = 0传递给cpu了 ,导致线程B使用的就旧的值 i = 0去进行操作,得到结果是错的。
怎样才能保证原子性?
小陈:那如果要保证原子性,应该是怎么样子的?
老王:如果要保证原子性的话,落到底层实际还是需要进行加锁的,需要保证任意时刻只能有一个线程能执行成功。
比如在硬件层次或者对总线进行加锁,使得某一时刻只能有一个线程能执行i 操作,这样才能是不被中断的,才是原子性的。
现在现在这种情况,相当于就是两个线程同时进行了 i 操作,线程A的 i 操作还没结束;线程B的 i 操作就也同时进行着,这种情况不是原子的。
小陈:哦,是不是可以这么理解:
如果要保证原子性的话,同一时刻只能有一个线程或者CPU能够执行成功,底层是需要对硬件进行加锁的,只有某个CPU或者线程锁定了,享有独占的权限,那么它的操作才能是不被其它CPU或者线程打断的。
老王:没错,就是这个道理;你只有在硬件级别加锁了之后,享有独占的权限;你的操作才能是不被其它CPU或线程打断的。
小陈:好的,老王,这么说我就理解了。
老王:这一篇对volatile不能保障原子性的解释,你再多看看几遍,多理解一下。再对之前的文章再复习复习,包括内存屏障、java内存模型、MESI一致性协议等知识,从下一章看是我们就要进入新的学习了。
小陈:好啊老王,下面的篇章我们要学习什么知识啊?
老王:下面我们就要进入synchronized的学习了....
目录
JAVA并发专题 《筑基篇》
1.什么是CPU多级缓存模型?
2.什么是JAVA内存模型?
3.线程安全之可见性、有序性、原子性是什么?
4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?
JAVA并发专题《练气篇》
5.volatile怎么保证可见性?
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线程池篇总结