volatile关键字解析

内存模型的概念

计算机执行程序时,每条指令都是在cpu中执行的,执行指令过程中,涉及到的数据的读写操作,程序运行过程中临时数据都保存在主存(物理内存)中,由于cpu的执行速度很快,所以数据的读取和向内存中的的写入相比较cpu的执行速度要慢很多,因此任何时候对数据的操作通过内存交互都会降低指令的执行速度,所以cpu里面产生了高速缓存。
对于多线程,在多核中,每条线程都运行于不同的cpu中,所以每个线程都有自己的高速缓存;程序在运行时,将运行数据从主存中复制一份到高速缓存中,cpu在对数据进行计算时可以直接从高速缓存中读取数据和向其中写入数据,当运行结束后,直接将结果写入主存中。当一个变量在多个cpu中都有缓存时,则可能造成缓存不一致问题。
解决缓存不一致的2种方式:
(1)通过在总线加Lock锁;
因为在早期cpu与其他部件进行通信是通过总线进行的,如果对总线进行Lock锁,则阻塞了其他cpu对其他部件的访问,则只能有一个cpu访问这个变量的内存,
缺点:在总线加锁期间,其他cpu无法访问内存,导致效率低下。
(2)通过缓存一致性协议;MESI协议中保证了每个缓存中保存的共享变量的副本都是一致性的。实质是:当cpu写数据时,发现操作的变量是共享变量(其他cpu中也存在该变量),则通知其他cpu将该变量的缓存行置为无效状态,所以当其他变量读取该变量时,发现该变量在缓存中的缓存行是无效状态,所以直接去内存中重新读取。
并发过程中的原子性、可见性、有序性
原子性:一个操作或多个操作,要么全部执行且执行过程中不被任何因素打断,要么不执行;
可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,则其他线程能够看到修改后的值;
有序性:程序的执行顺序按照代码的执行顺序;
指令重排序:处理器为了提高程序运行效率,对输入代码进行优化,则不保证程序中各个语句的执行顺序同代码中的执行顺序一致,但是会保证代码的最终执行结果与代码顺序的执行结果一致;所以处理器对代码进行指令重排,但是会保证程序的最终结果和代码顺序执行结果顺序一致,指令重排时,不能对有数据依赖性的语句进行重排,即指令重排语句前后对最终执行结果没有影响,
注:指令重排不会影响单线程的执行,但是会影响多线程并发执行的正确性。

Java内存模型

Java内存模型:规定所有变量都保存在主存中,每个线程都有自己的工作内存,线程对变量的所有操作都在工作内存中进行,而不能直接对主存进行操作,且每个线程不能访问其他线程的工作内存。
原子性:在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。
Java内存模型只保证了基本读取和赋值是原子操作,如i++等自增操作则实质上分为三步骤:读取x的值,进行加1操作,写入新值;所以不具有原子操作。
由于synchronized和Lock保证了同一时刻只有一个线程执行该代码,从而保证原子性。
可见性:当一个变量被volatile修饰时,会保证修改后的值立即被更新到主存,当有其他线程需要时,则直接去内存中读取新值。
普通变量不能保证可见性是因为:普通变量被修改后不知道什么时候才能被更新到内存中,当有其他线程访问时,此时的变量还没有被更新,所以读到的是旧值,即无法保证可见性。
由于synchronized和Lock保证了同一时刻只有一个线程执行该代码,在释放锁之前会将修改值刷新到主存中,因此可以保证可见性。
有序性:Java内存模型中的happens-before原则可以先天性的保证有序性,如果两个操作无法保证从happens-before原则中推导出来,则虚拟机可以对其进行任意的重排序。
由于synchronized和Lock保证了同一时刻只有一个线程执行该代码,相当于让线程顺序执行代码,所以保证了有序性。
一个共享变量被volatile修饰之后,则具有两层意义:
(1)保证了不同线程对用一个变量进行操作时的可见性,即一个线程修改了某变量的值之后,其他线程均可以看到此变量修改后的新值。
(2)禁止指令重排;
volatile修饰共享变量之后:
(1)强制将修改后的值立即保存到内存中;
(2)当线程2进行执行时会导致线程1工作内存中的stop缓存行无效;
(3)由于线程1工作内存中的stop缓存行无效,所以线程1再次读取变量stop中的值会到主存中读取;
所以volatile修饰共享变量保证了操作的可见性;
由于自增操作不是原子性操作,而且volatile也无法保证对变量的任何操作都是原子性的。所以volatile修饰共享变量无法保证操作的原子性;
volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。
volatile关键字禁止指令重排序有两层意思:
(1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
(2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

详见:https://www.cnblogs.com/dolphin0520/p/3920373.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值