专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

volatile关键字在并发中有哪些作用?

  由于计算机为了充分利用CPU的高性能,以及各个硬件存取速度巨大的差异带来的一系列问题为了充分压榨CPU的性能,CPU会对指令乱序执行或者语言的编译器会指令重排,让CPU一直工作不停歇,但同时会导致有序性问题。为了平衡CPU的寄存器和内存的速度差异,计算机的CPU增加了高速缓存,但同时导致了可见性问题为了平衡CPU与IO设备的速度差异,操作系统增加了进程、线程概念,以分时复用CPU,但同时导致了原子性问题。
  Java是最早尝试提供内存模型的编程语言。由于Java语言是跨平台的,另外各个操作系统总存在一些差异,Java在物理机器的基础上抽象出一个内存模型(JMM),来简化和管理并发程序。我们都知道Java并发的三大特性:原子性,可见性,有序性原子性指的是一个不可以被分割的操作,即这个操作在执行过程中不能被中断,要么全部不执行,要么全部执行。且一旦开始执行,不会被其他线程打断。可见性指的是一个线程修改了共享变量后,其他线程能立即感知这个变量被修改。有序性指程序按照代码的先后顺序执行。在Java内存模型中,为了提升效率是允许编译器和处理器对指令进行重排序,当然重排序不会影响单线程的运行结果,但是对多线程会有影响
  那么本文我们就聊聊关键字volatile,可能是Java中最微妙和最难用的关键字,看看其在Java内存模型中是如何保证并发操作的原子性、可见性、有序性的?什么是volatile关键字
  volatile是Java中用于修饰变量的关键字,其可以保证该变量的可见性以及顺序性,但是无法保证原子性。更准确地说是volatile关键字只能保证单操作的原子性,比如x1,但是无法保证复合操作的原子性,比如x
  其为Java提供了一种轻量级的同步机制:保证被volatile修饰的共享变量对所有线程总是可见的,也就是当一个线程修改了一个被volatile修饰共享变量的值,新值总是可以被其他线程立即得知。相比于synchronized关键字(synchronized通常称为重量级锁),volatile更轻量级,开销低,因为它不会引起线程上下文的切换和调度。保证可见性
  可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。我们一起来看一个例子:publicclassVisibilityTest{privatebooleanflagtrue;publicvoidchange(){flagfalse;System。out。println(Thread。currentThread()。getName(),已修改flagfalse);}publicvoidload(){System。out。println(Thread。currentThread()。getName(),开始执行。。。。。);inti0;while(flag){i;}System。out。println(Thread。currentThread()。getName(),结束循环);}publicstaticvoidmain(String〔〕args)throwsInterruptedException{VisibilityTesttestnewVisibilityTest();线程threadA模拟数据加载场景ThreadthreadAnewThread(()test。load(),threadA);threadA。start();让threadA执行一会儿Thread。sleep(1000);线程threadB修改共享变量flagThreadthreadBnewThread(()test。change(),threadB);threadB。start();}}
  其中:threadA负责循环,threadB负责修改共享变量flag,如果flagfalse时,threadA会结束循环,但是上面的例子会死循环!原因是threadA无法立即读取到共享变量flag修改后的值。我们只需privatevolatilebooleanflagtrue;,加上volatile关键字threadA就可以立即退出循环了。
  其中Java中的volatile关键字提供了一个功能:那就是被volatile修饰的变量P被修改后,JMM会把该线程本地内存中的这个变量P,立即强制刷新到主内存中去,导致其他线程中的volatile变量P缓存无效,也就是说其他线程使用volatile变量P在时,都是从主内存刷新的最新数据。而普通变量的值在线程间传递的时候一般是通过主内存以共享内存的方式实现的;
  因此,可以使用volatile来保证多线程操作时变量的可见性。除了volatile,Java中的synchronized和final两个关键字以及各种Lock也可以实现可见性。加锁的话,当一个线程进入synchronized代码块后,线程获取到锁,会清空本地内存,然后从主内存中拷贝共享变量的最新值到本地内存作为副本,执行代码,又将修改后的副本值刷新到主内存中,最后线程释放锁。保证有序性
  有序性,顾名思义即程序执行的顺序按照代码的先后顺序执行。但现代的计算机中CPU中为了能够让指令的执行尽可能地同时运行起来,提示计算机性能,采用了指令流水线。一个CPU指令的执行过程可以分成4个阶段:取指、译码、执行、写回。这4个阶段分别由4个独立物理执行单元来完成。
  理想的情况是:指令之间无依赖,可以使流水线的并行度最大化但是如果两条指令的前后存在依赖关系,比如数据依赖,控制依赖等,此时后一条语句就必需等到前一条指令完成后,才能开始。所以CPU为了提高流水线的运行效率,对无依赖的前后指令做适当的乱序和调度,即现代的计算机中CPU是乱序执行指令的
  另一方面,只要不会改变程序的运行结果,Java编译器是可以通过指令重排来优化性能。然而,重排可能会影响本地处理器缓存与主内存交互的方式,可能导致在多线程的情况下发生细微的BUG。
  指令重排一般可以分为如下三种类型:编译器优化重排序,编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。指令级并行重排序,现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。内存系统重排序,由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。这并不是显式的将指令进行重排序,只是因为缓存的原因,让指令的执行看起来像乱序。
  从Java源代码到最终执行的指令序列,一般会经历下面三种重排序:
  变量初始化赋值
  我们一起来看一个例子,让大家体悟volatile关键字的禁止指令重排的作用:inti0;intj0;intk0;i10;j1;
  对于上面的代码我们正常的执行流程是:
  初始化i初始化j初始化ki赋值j赋值
  但由于指令重排序问题,代码的执行顺序未必就是编写代码时候的顺序。语句可能的执行顺序如下:
  初始化ii赋值初始化jj赋值初始化k
  指令重排对于非原子性的操作,在不影响最终结果的情况下,其拆分成的原子操作可能会被重新排列执行顺序,提升性能。指令重排不会影响单线程的执行结果,但是会影响多线程并发执行的结果正确性。但当我们用volatile修饰变量k时:inti0;intj0;volatileintk0;i10;j1;
  这样会保证上面代码执行顺序:变量i和j的初始化,在volatileintk0之前,变量i和j的赋值操作在volatileintk0后面懒汉式单例双重校验锁volatile版
  我们可以使用volatile关键字去阻止重排volatile变量周围的读写指令,这种操作通常称为memorybarrier(内存屏障),详情可见:mp。weixin。qq。comsTyiCfVMee中懒汉式单例双重校验锁volatile版隐藏特性
  volatile关键字除了禁止指令重排的作用,还有一个特性:当线程向一个volatile变量写入时,在线程写入之前的其他所有变量(包括非volatile变量)也会刷新到主内存。当线程读取一个volatile变量时,它也会读取其他所有变量(包括非volatile变量)与volatile变量一起刷新到主内存。尽管这是一个重要的特性,但是我们不应该过于依赖这个特性,来自动使周围的变量变得volatile,若是我们想让一个变量是volatile的,我们编写程序的时候需要非常明确地用volatile关键字来修饰。无法保证原子性
  volatile关键字无法保证原子性,更准确地说是volatile关键字只能保证单操作的原子性,比如x1,但是无法保证复合操作的原子性,比如x
  所谓原子性:即一个或者多个操作作为一个整体,要么全部执行,要么都不执行,并且操作在执行过程中不会被线程调度机制打断;而且这种操作一旦开始,就一直运行到结束,中间不会有任何上下文切换(contextswitch)int0;语句1,单操作,原子性的操作i;语句2,复合操作,非原子性的操作
  其中:语句2i其实在Java中执行过程,可以分为3步:i被从局部变量表(内存)取出,压入操作栈(寄存器),操作栈中自增使用栈顶值更新局部变量表(寄存器更新写入内存)
  执行上述3个步骤的时候是可以进行线程切换的,或者说是可以被另其他线程的这3步打断的,因此语句2不是一个原子性操作volatile版i
  我们再来看一个例子:publicclassTest1{publicstaticvolatileintval;publicstaticvoidadd(){for(inti0;i1000;i){val;}}publicstaticvoidmain(String〔〕args)throwsInterruptedException{Threadt1newThread(Test1::add);Threadt2newThread(Test1::add);t1。start();t2。start();t1。join();等待该线程终止t2。join();System。out。println(val);}}
  2个线程各循环2000次,每次1,如果volatile关键字能够保证原子性,预期的结果是2000,但实际结果却是:1127,而且多次执行的结果都不一样,可以发现volatile关键字无法保证原子性。synchronized版i
  我们可以利用synchronized关键字来解决上面的问题:publicclassSynchronizedTest{publicstaticintval;publicsynchronizedstaticvoidadd(){for(inti0;i1000;i){val;}}publicstaticvoidmain(String〔〕args)throwsInterruptedException{Threadt1newThread(SynchronizedTest::add);Threadt2newThread(SynchronizedTest::add);t1。start();t2。start();t1。join();等待该线程终止t2。join();System。out。println(val);}}
  运行结果:2000Lock版i
  我们还可以通过加锁来解决上述问题:publicclassLockTest{publicstaticintval;staticLocklocknewReentrantLock();publicstaticvoidadd(){for(inti0;i1000;i){lock。lock();上锁try{val;}catch(Exceptione){e。printStackTrace();}finally{lock。unlock();解锁}}}publicstaticvoidmain(String〔〕args)throwsInterruptedException{Threadt1newThread(LockTest::add);Threadt2newThread(LockTest::add);t1。start();t2。start();t1。join();等待该线程终止t2。join();System。out。println(val);}}
  运行结果:2000Atomic版i
  Java从JDK1。5开始提供了java。util。concurrent。atomic包(以下简称Atomic包),这个包中的原子操作类,靠CAS循环的方式来保证其原子性,是一种用法简单、性能高效、线程安全地更新一个变量的方式。
  这些类可以保证多线程环境下,当某个线程在执行atomic的方法时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个线程执行。
  我们来用atomic包来解决volatile原子性的问题:publicclassAtomicTest{publicstaticAtomicIntegervalnewAtomicInteger();publicstaticvoidadd(){for(inti0;i1000;i){val。getAndIncrement();}}publicstaticvoidmain(String〔〕args)throwsInterruptedException{Threadt1newThread(AtomicTest::add);Threadt2newThread(AtomicTest::add);t1。start();t2。start();t1。join();等待该线程终止t2。join();System。out。println(val);}}
  运行结果:2000,如果我们维护现有的项目,如果遇到volatile变量最好将其替换为Atomic变量,除非你真的特别了解volatile。Atomic就不展开说了,先挖个坑,以后补上volatile原理
  当大家仔细读完上文的懒汉式单例双重校验锁volatile版,会发现volatile关键字修饰变量后,我们反汇编后会发现多出了lock前缀指令,lock前缀指令在汇编中LOCK指令前缀功能如下:被修饰的汇编指令成为原子的与被修饰的汇编指令一起提供内存屏障效果(lock指令可不是内存屏障)
  内存屏障主要分类:一类是可以强制读取主内存,强制刷新主内存的内存屏障,叫做Load屏障和Store屏障另一类是禁止指令重排序的内存屏障,主要有四个分别叫做LoadLoad屏障、StoreStore屏障、LoadStore屏障、StoreLoad屏障
  这4个屏障具体作用:LoadLoad屏障:(指令Load1;LoadLoad;Load2),在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。LoadStore屏障:(指令Load1;LoadStore;Store2),在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。StoreStore屏障:(指令Store1;StoreStore;Store2),在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。StoreLoad屏障:(指令Store1;StoreLoad;Load2),在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能
  对于volatile操作而言,其操作步骤如下:每个volatile写入之前,插入一个StoreStore,写入以后插入一个StoreLoad每个volatile读取之前,插入一个LoadLoad,读取之后插入一个LoadStore
  我们再总结以下,用volatile关键字修饰变量后,主要发生的变化有哪些?:当一个线程修改了volatile修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新值。即volatile关键字保证了并发的可见性
  使用volatile关键字修饰共享变量后,每个线程要操作该变量时会从主内存中将变量拷贝到本地内存作为副本,但当线程操作完变量副本,会强制将修改的值立即写入主内存中。然后通过CPU总线嗅探机制告知其他线程中该变量副本全部失效,(在CPU层,一个处理器的缓存回写到内存会导致其他处理器的缓存行无效),若其他线程需要该变量,必须重新从主内存中读取。在x86的架构中,volatile关键字底层含有lock前缀的指令,与被修饰的汇编指令一起提供内存屏障效果,禁止了指令重排序,保证了并发的有序性
  确保一些特定操作执行的顺序,让cpu必须按照顺序执行指令,即当指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;volatile关键字无法保证原子性,更准确地说是volatile关键字只能保证单操作的原子性,比如x1,但是无法保证复合操作的原子性,比如x。有人可能问赋值操作是原子操作,本来就是原子性的,用volatile修饰有什么意义?在Java数据类型足够大的情况下(在Java中long和double类型都是64位),写入变量的过程分两步进行,就会发生Wordtearing(字分裂)情况。JVM被允许将64位数量的读写作为两个单独的32位操作执行,这增加了在读写过程中发生上下文切换的可能性,多线程的情况下可能会出现值会被破坏的情况
  在缺乏任何其他保护的情况下,用volatile修饰符定义一个long或double变量,可阻止字分裂情况

最新!新能源车保值率排名,小鹏发挥余热,比亚迪垄断插混榜!新能源车由于不用拍牌养车成本低等优点,近两年成为汽车市场的宠儿,在国内汽车总销量上占据半壁江山,我国也因为大力发展新能源车,成为新能源车第一大国。但是新能源车也有着续航焦虑(纯电车原油交易提醒油价受累于美国产量增加,沙特将下调运往亚洲的原油价格11月1日(周二)亚市盘初,美油交投于86。17美元桶附近因油价周一下跌超2,美国产量可能增加,疫情蔓延影响需求,沙特或于12月下调运往亚洲的原油价格也拖累油价。影响油价利空因素聚激战!乌22次空袭俄军阵地!袭击佩洛西丈夫嫌疑人拒不认罪!普京调查俄船遭袭!泽连斯基乌40能源基础设施遭破坏美股全线下跌当地时间11月1日,欧洲主要股指全线上涨,英国富时100指数涨逾1。美股高开低走,三大指数全线下跌,纳斯达克指数跌近1,明星科技股亚马逊苹果微软谷歌等悉数下跌,亚马逊跌逾5,市值跌冯导的美国梦大家晚上好,我是咚咚哐。冯小刚润(run)了上了热搜,原因是冯导被拍到在美国豪宅聚餐,又查到他在洛杉矶拥有几栋千万美金豪宅,为了保住自己的美国绿卡,冯小刚每年都会去美国待上几个月。飞天奖星光奖双双出炉山海情海报觉醒年代剧照典籍里的中国海报昨晚,第33届电视剧飞天奖第27届电视文艺星光奖颁奖典礼在北京首钢冰球馆举行。本届飞天奖共46部作品入围,最终,觉醒年代山海情功勋等16部作品天道酬勤不酬菜只因,DK是谁在C?没错就是Beryl提到Beryl,大家对他的印象就是,原神哥,冥想训练,DK的大脑。S9,Beryl和DWG进入全球总决赛,拿到八强成绩S10,Beryl和DWG拿到冠军S11,Beryl和DK拿到弹壳特工队BOOS攻略易爆虫贝塔前言大家好,我是烟雨,最近烟雨看私信中有很多人问烟雨易爆虫该怎么打,自己一直打不过,问烟雨有没有什么技巧,下面烟雨就来将一下这个BOOS该怎么打。我们一起往下看。BOOS攻略BOOV5前教练终极爆料RNG射野留不住教练回EDG,IG直接卖队了头条创作挑战赛2022电竞季随着世界赛逐渐走向尾声,咱们LPLS12赛季的征程也就结束了。这次世界赛算是近五年来咱们LPL拿到的最差战绩,一个16强两个8强,一个4强。依然感觉有点大魔王归来!Faker状态回春风评逆转,或能冲刺第四个总冠军世界赛前,几乎没有人看好已经迟暮之年的faker,人们都觉得这位老将的S12无非是再一次对命运的无奈挣扎,大飞老师是人们对他的嘲笑,可随着T1用极为碾压的方式战胜JDG战队后,所有2号更新突变,免费史诗上线,孙悟空笑了,赵云项羽重做巨帅可乐要加冰,乔妹在你心,大家好我是小乔妹,每天为您分享有趣的王者荣耀新皮肤新版本,最新攻略等文章内容。王者荣耀将会在11月2日再次进行一次更新了,这次更新以后,游戏内还会开启一些新如何评价原神角色演示纳西妲无垠无忧?近日,原神进行了角色演示纳西妲无垠无忧,纳西妲完美流畅3D转2D立绘的操作已经是原神的家常便饭不说了。演示里先是把小草神的设计元素一一展露了一遍,荡秋千,最受孩童欢迎的运动方式,有
爷爷前两天,88岁的爷爷没能抗过这次疫情,走完了自己孤独又惨淡的一生一早爸爸打电话来告诉我爷爷没了,让我看看能不能给他找到顺风车将他送到三百公里外的爷爷家,听后心中冰凉,我已好些年没去民政大小事一分钟知晓(2022年12月22日)大家好!今天是2022年12月22日,农历十一月廿九,星期四,这些与民政相关的资讯,值得关注!要闻12月20日,国务院总理李克强主持召开国务院常务会议,提出做好困难群众生活保障,该学习贯彻党的二十大精神,云浮这场培训班直达基层一线12月22日,云浮市举办2022年全市基层党组织书记党务工作者党代表学习贯彻党的二十大精神示范培训班。培训班以视频形式举办,开至县镇村三级,全市6623名各领域基层党组织书记党务工喜讯!花都5个农产品上榜全国名特优新农产品名录近日,农业农村部农产品质量安全中心公布2022年第三批全国名特优新农产品名录,其中,广州市花都区的5个农产品赫然在列,它们分别是花都蓝莓花都无花果花都西洋菜花都彩虹鲷和花都加州鲈,惠及超2000个个体工商户,三水新增三个秒批秒办事项为深入推动放管服改革,优化营商环境,进一步优化企业和群众的政务服务体验,近日,佛山市三水区政务数据局与烟草专卖局联合对烟草零售许可证补办歇业恢复营业3个事项业务实现秒批秒办。群众通非人哉铁扇公主私会大士,牛魔王发现真相,老婆爱的不是自己!非人哉中有位女性角色在初登场的时候就非常惊艳,那就是红孩儿的母亲牛魔王的老婆孙悟空的嫂子铁扇公主,这位公主不光人长得好看气质高贵,还在御姐范中透露着一股可爱劲,最重要的是人家还有霸关于蜂蜜与熊的相遇成为母亲以后,我经常想一个问题,大人和小孩之间,抛开爱责任义务,有没有可能存在真正的友谊?虽然身为母亲,但我并不奢望成为孩子最好的朋友扪心自问,我也不想和我妈做朋友,母女之间总会有2022年11月河北省对一带一路沿线国家进出口增长27。6冀时客户端报道(河北台苏杭通讯员刘宏灵周俊伟)石家庄海关统计数据显示今年11月,河北对一带一路沿线国家(以下简称沿线国家)进出口205。6亿元人民币(下同),同比(下同)增长27。入选工信部工业互联平台创新案例!深圳这家智慧停车龙头企业如何做到的?近日,国家工业和信息化部发布2022年工业互联网平台创新领航应用案例名单,全国共137家入选,其中深圳市6家,3家为龙华区内企业。由捷顺科技打造的基于工业互联网平台的智慧停车及服务微视频生态中国四季之美东风送暖山川河流初解冻蛰虫始振闻风欲动鱼儿欢游冰雪消融草木初萌万物复苏和风暖阳莺飞草长杨柳青千花百卉竞相争媚玄鸟归来莺燕啼暖春雷乍响万物生长阵阵蛙鸣草木生灵向阳生雨水滋养藤柳攀高生生态中国四季之美东风送暖山川河流初解冻蛰虫始振闻风欲动鱼儿欢游冰雪消融草木初萌万物复苏和风暖阳莺飞草长杨柳青千花百卉竞相争媚玄鸟归来莺燕啼暖春雷乍响万物生长阵阵蛙鸣草木生灵向阳生雨水滋养藤柳攀高生
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网