Java volatile的理解

volatile的理解

我们会从硬件和软件层理解

对于volatile理解之前,我需要先知道一些知识,多线程环境下怎样才能保证共享变量的线程安全那?
可见性
原子性
有序性

那什么是可见性那?什么是原子性那?有序性又是什么那?

硬件层


在理解在三个概念的时候,我们需要了解一下cpu硬件的一些相关概念。
cpu在发展中由单核 发展为了 多核,如果单核的就不会出现问题,如果是多核就会出现问题
我们都知道cpu的计算速度会非常快,需要从内存里面取数据。从内存取数据的速度是比较慢的,内存的速度远远不能赶上cpu的运算速度,造成了cpu长时间空闲状态(cpu饥饿)。
为了解决这个问题,解决方案:加一个高速缓存区域,接近cpu的运行速度。cpu从高速缓存拿数据,同时存放一些热点数据。(补充一点,cpu还有寄存器用来存放中间计算值)。
当然解决方案虽然解决了cpu一定程度的饥饿问题,但是也带来缓存和主存数据不一致的问题。

看到这里,是不是觉得有一万个草泥马奔腾而过 ,这简直太坑了,一个解决方案解决一定程度cpu饥饿和运行速度,又造出来一个新的问题。
不过也确实蛮坑的,解决方案并不是完美的嘛,那我们能怎么办,我们无解啊,只能学习另一个解决方案了,木得办法。跟我们平时写bug一样一样的。
当然 cpu厂商肯定也知道这个问题了,开始给的解决方案是cpu总线加锁,但是加锁会影响效率嘛,后来提出mesi等相关一些协议的解决方案来保持一致性。

cpu历史大致流程:

cpu 单核 问题:计算能力有限 解决方案:增加cpu物理晶体 (个人能力还不特别出众)

CPU 单核晶体的不断增加(摩尔定律) 问题:计算能力上升,能源消耗大,温度高,物理硬件出现瓶颈 解决方案:cpu 单核多模设计(个人能力购买硬件提升,单人能力很强)

CPU 单核多模处理模型 问题:计算能力相对有一些牺牲,物理硬件运行比较稳定,一定时间真的还不赖那,但是我们应用程序越来越大,需要更强算力。 (相当于个人提高文化素养)
                     解决方案:一个核心不够,我就组装多个嘛(一个人能力有限,找多个人合作)

CPU 多核处理 问题:cpu算力总算是提高上来了,但是取数据又特别慢(多个人合作无敌状态)
             解决方案:cpu开始膨胀了,总觉取数据太慢了,能不能给我快点的,但是内存老弟现在硬件水平就这样,没有达到cpu有钱开挂的水平啊
                       cpu妥协了,加一个接近我的区域吧,你们只要把数据放到这里就行,我给你做计算。

CPU 多核处理 + 高速缓存 问题:又出现了缓存和内存出现了不一致的问题(多个人同时看一眼数据,开始计算,然后再返回结果),核之间产生了隔离
                        解决方案:cpu那就加锁吧,谁先拿到这个锁,我就给谁运算

CPU 多核处理 + 高速缓存 + 加锁 问题 :加锁就串行化了,影响效率
                               解决方案:cpu我太难了,加锁也不行,那我就加一个管理员吧,他来监控处理数据可见性,
                                         任何一个核心修改了数据,就让给必须给内存
                                         任何一个核心读数据,就必须从内存中读取

CPU 多核处理 + 高速缓存 + 核心管理员 问题:cpu计算的时候发现,有一些数据之间没有依赖性,还是按照先后顺序执行的有些慢
                                    解决方案:无所谓先后顺序,先后顺序不影响计算结果的,cpu我去做优化排序了。
                                    
CPU 多核处理 + 高速缓存 + 核心管理员 + 指令乱序优化 问题:虽然优化了我们的指令,但是有时候我们不想让优化和优化后,对于其他核心有影响
                                                    解决方案:CPU顿时难过了,合着优化一下还不行,给你一个指令(内存屏障),告诉我那些不需要优化
                                                    
            
以上大概就是我们cpu一个发展过程,纯属个人理解,如有不对,欢迎大家指教。
                        
说了这么多,还是没有跟 valatile 有什么关系啊,不要着急,以上是硬件层,下面我们从软件层理解

 

软件层


因为硬件的厂商不同,可能使用相关技术实现细节不一样,但是结果导向是基本都是一致的。
java 为了解决平台的差异性,使用 jvm 来让java 程序员不用感知平台差异性,只要符合我java的规范就可以,jvm封装操作系统还有硬件的相关细节,你只要学习我java规范就可以

没错就是这样 java对于内存管理 经过了层层抽象,形成了java内存管理模型(JMM)

大致模型:
线程工作内存 主存

那么线程工作内存和主存对应的是cpu缓存和内存吗?
其实也不是完全这样的
java 线程工作内存 会对应 主存,cpu缓存 等
java 主存 会对应 主存,cpu缓存 等
只不过是jvm对内存管理都已经封装起来了,具体细节,笔者也不太懂,需要加强知识啊。

重点来了,volatile就是java层面来实现,跟平台没有太大关系(不管你的平台是linux、还是windows等都么有关系,底层由jvm来完成和平台之间交互,得到的结果导向是一致的)
volatile 可见性原理 是jmm协议规范指定:1、线程工作内存完成共享变量写操作,强制刷新到主存,且其他线程读到共享变量
                                   2、线程工作内存读操作,强制从主存读取

volatile 有序性(禁止指令重排)原理 是内存屏障:内存屏障指令其实针对硬件指令架构来一层一层封装出来的
比如 java抽象出来字节码就是load ,当jvm遇到load字节码指令的时候,转换成cpu的禁止指令重排的指令,就不会出现指令的重排。
volatile 其实不能保证原子性的,但是针对单一的操作其实可以的(i=1)是有原子性的,非单一操作其实不具有原子性(如:i++)     

以下是java线程内存模型,帮助大家理解

 

Java volatile的理解