首页 > 其他分享 >volatile为什么不能保证原子性

volatile为什么不能保证原子性

时间:2023-06-01 17:46:05浏览次数:31  
标签:volatile 变量 主存 原子 缓存 保证 线程

首先要了解的是,volatile可以保证可见性和顺序性,这些都很好理解,那么它为什么不能保证原子性呢?

可见性

可见性与Java的内存模型有关,模型采用缓存与主存的方式对变量进行操作,也就是说,每个线程都有自己的缓存空间,对变量的操作都是在缓存中进行的,之后再将修改后的值返回到主存中,这就带来了问题,有可能一个线程在将共享变量修改后,还没有来的及将缓存中的变量返回给主存中,另外一个线程就对共享变量进行修改,那么这个线程拿到的值是主存中未被修改的值,这就是可见性的问题。

volatile很好的保证了变量的可见性,变量经过volatile修饰后,对此变量进行写操作时,汇编指令中会有一个LOCK前缀指令,这个不需要过多了解,但是加了这个指令后,会引发两件事情:

  • 将当前处理器缓存行的数据写回到系统内存
  • 这个写回内存的操作会使得在其他处理器缓存了该内存地址无效
    什么意思呢?意思就是说当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值,这就保证了可见性。

原子性

问题来了,既然它可以保证修改的值立即能更新到主存,其他线程也会捕捉到被修改后的值,那么为什么不能保证原子性呢?
首先需要了解的是,Java中只有对基本类型变量的赋值和读取是原子操作,如i = 1的赋值操作,但是像j = i或者i++这样的操作都不是原子操作,因为他们都进行了多次原子操作,比如先读取i的值,再将i的值赋值给j,两个原子操作加起来就不是原子操作了。

所以,如果一个变量被volatile修饰了,那么肯定可以保证每次读取这个变量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了。

举个栗子

一个变量i被volatile修饰,两个线程想对这个变量修改,都对其进行自增操作也就是i++,i++的过程可以分为三步,首先获取i的值,其次对i的值进行加1,最后将得到的新值写会到缓存中。
线程A首先得到了i的初始值100,但是还没来得及修改,就阻塞了,这时线程B开始了,它也得到了i的值,由于i的值未被修改,即使是被volatile修饰,主存的变量还没变化,那么线程B得到的值也是100,之后对其进行加1操作,得到101后,将新值写入到缓存中,再刷入主存中。根据可见性的原则,这个主存的值可以被其他线程可见。
问题来了,线程A已经读取到了i的值为100,也就是说读取的这个原子操作已经结束了,所以这个可见性来的有点晚,线程A阻塞结束后,继续将100这个值加1,得到101,再将值写到缓存,最后刷入主存,所以即便是volatile具有可见性,也不能保证对它修饰的变量具有原子性。

标签:volatile,变量,主存,原子,缓存,保证,线程
From: https://www.cnblogs.com/cheng20008100/p/17449703.html

相关文章

  • Java并发之原子性、可见性和有序性
    1.原子性1.1原子性的定义原子性:原子性即是一个或者多个操作,要么全程执行,并且执行的过程中不被任何因素打断,要么全部不执行。举个例子会更好理解:就像是我们去银行转账的时候,A给B转1000元,如果A的账户减少了1000之后,那么B的账户一定要增加1000。A的账户减钱,B的账户加钱,这两个操作......
  • Java的volatile
    介绍volatilevolatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但是它并不容易被正确、完整地理解,以至于许多程序员都习惯去避免使用它,遇到需要处理多线程数据竞争问题的时候一律使用synchronized来进行同步。了解volatile变量的语义对理解多线程操作的其他特性......
  • Java的volatile
    介绍volatilevolatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但是它并不容易被正确、完整地理解,以至于许多程序员都习惯去避免使用它,遇到需要处理多线程数据竞争问题的时候一律使用synchronized来进行同步。了解volatile变量的语义对理解多线程操作的其他特性......
  • 请访问我的个人blog,这边的随笔不保证更新维护
    在此,和大家说声抱歉。我在博客园这边发过的随笔,不保证更新维护。当然,我会保留个人认为还算有点参考价值的博文。如果你觉得我的博文能给你带来帮助,可以看我的个人博客:https://blog.cnwangk.top/......
  • 对volatile的理解
    volatile保证了线程之间的可见性。即某个线程修改了一个共享变量,这个变量立刻对所有线程可见。volatile保证了指令重排序:用volatile修饰的共享变量在读、写时会加入一个内存屏障,保证了指令重排序。volatile可以保证可见性、有序性,但是不能保证原子性。......
  • 新建T1,T2,T3线程,如何保证它们执行的顺序性
    在多线程中有多种方法让线程按特定顺序执行,可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。  ......
  • 八、JUC-原子类
    一、基本类型原子类首先,我们来用原子类实现i++的效果,可以看出,结果并不是50000packagecom.lori.juc2023.juc8;importjava.util.concurrent.atomic.AtomicInteger;publicclassAtomicDemo1{publicstaticfinalintSIZE=50;publicstaticvoidmain(Strin......
  • 正点原子imx6ull通过网线连接电脑并使用MobaXterm登录
    板子上配置静态IP注意:本文所用软件为SecureCRT与Mobaxterm 首先我们通过串口线将电脑与imx6ull进行连接。并在电脑上通过SecureCRT进入板子的linux系统①修改静态IP我们使用vi/etc/network/interfaces命令进入这个路径下的文件进行编辑,如下图把文件里面的#注释符删除就行了,当然......
  • 正点原子imx6ull中sudo命令失效问题
    问题出现事情的起因是这样的,我的imx6ull的板子很久没用了,这次重新上电之后,我习惯性的敲了一个sudo随后就发现报错sudo:errorin/etc/sudo.conf,line0whileloadingplugin`sudoers_policy'sudo:/usr/libexec/sudo/sudoers.somustbeownedbyuid0sudo:fatalerror,un......
  • 【Azure 媒体服务】Azure Media Service上传的视频资产,如何保证在Transfer编码后音频
    问题描述AzureMediaService上传的视频资产,如何保证在Transfer编码后音频文件和视频文件不分成两个文件?保持在一个可以直接播放的MP4文件中呢? 问题解答AzureMediaService上提供的Build-inTransform生成的资产中,音频与视频分别存储在不同的文件中。通过自定义StandardEncode......