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

深入理解关键字volatile

  volatile关键字的作用是什么?
  相比于synchronized关键字(重量级锁)对性能影响较大,Java提供了一种较为轻量级的可见性和有序性问题的解决方案,那就是使用volatile关键字。由于使用volatile不会引起上下文的切换和调度,所以volatile对性能的影响较小,开销较低。
  从并发三要素的角度看,volatile可以保证其修饰的变量的可见性和有序性,无法保证原子性(不能保证完全的原子性,只能保证单次读写操作具有原子性,即无法保证复合操作的原子性)。
  下面将从并发三要素的角度介绍volatile如何做到可见和有序的。1。volatile如何实现可见性?
  什么是可见性?
  可见性指当多个线程同时访问共享变量时,一个线程对共享变量的修改,其他线程可以立即看到(即任意线程对共享变量操作时,变量一旦改变所有线程立即可以看到)。1。1可见性例子volatile可见性例子author单程车票publicclassVisibilityDemo{构造共享变量publicstaticbooleanflagtrue;publicstaticvolatilebooleanflagtrue;如果使用volatile修饰则可以终止循环publicstaticvoidmain(String〔〕args){线程1更改flagnewThread((){睡眠3秒确保线程2启动try{TimeUnit。SECONDS。sleep(3);}catch(InterruptedExceptione){e。printStackTrace();}修改共享变量flagfalse;System。out。println(修改成功,当前flag为true);},one)。start();线程2获取更新后的flag终止循环newThread((){while(flag){}System。out。println(获取到修改后的flag,终止循环);},two)。start();}}不使用volatile修饰flag变量时,运行程序会进入死循环,也就是说线程1对flag的修改并没有被线程2读到,也就是说这里的flag并不具备可见性。使用volatile修饰flag变量时,运行程序会终止循环,打印提示语句,说明线程2读到了线程1修改后的数据,也就是说被volatile修饰的变量具备可见性。1。2volatile如何保证可见性?
  被volatile修饰的共享变量flag被一个线程修改后,JMM(Java内存模型)会把该线程的CPU内存中的共享变量flag立即强制刷新回主存中,并且让其他线程的CPU内存中的共享变量flag缓存失效,这样当其他线程需要访问该共享变量flag时,就会从主存获取最新的数据。
  所以通过volatile修饰的变量可以保证可见性。
  两点疑问及解答:为什么会有CPU内存?为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1L2其他)后再进行操作,但是操作完后的数据不知道何时才会写回主存。所以如果是普通变量(未被修饰的),什么时候被写入主存是不确定的,所以读取的可能还是旧值,因此无法保证可见性。各个线程的CPU内存是怎么保持一致性的?实现了缓存一致性协议(MESI),MESI在硬件上约定了:每个处理器通过嗅探在总线上传播的数据来检查自己的CPU内存的值是否过期,当处理器发现自己的缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置为无效状态。当处理器对该数据进行修改操作时,会重新从系统内存(主存)中把数据读到处理器缓存(CPU内存)里。1。3volatile实现可见性的原理
  原理一:Lock指令(汇编指令)
  通过上面的例子的Class文件查看汇编指令时,会发现变量有无被volatile修饰的区别在于被volatile修饰的变量会多一个lock前缀的指令。
  lock前缀的指令会触发两个事件:将当前线程的处理器缓存行(CPU内存的最小存储单元,这里可以大致理解为CPU内存)的数据写回到主存(系统内存)中写回主存的操作会使其他线程的CPU内存中该内存地址的数据无效(缓存失效)
  所以使用volatile修饰的变量在汇编指令中会有lock前缀的指令,所以会将处理器缓存的数据写回主存中,同时使其他线程的处理器缓存的数据失效,这样其他线程需要使用数据时,会从主存中读取最新的数据,从而实现可见性。
  原理二:内存屏障(CPU指令)
  volatile的可见性实现除了依靠上述的LOCK指令(汇编指令)还依靠内存屏障(CPU指令)。
  为了性能优化,JMM在不改变正确语义的前提下,会允许编译器和处理器对指令序列进行重排序。JMM提供了内存屏障阻止这种重排序。
  这里介绍的是内存屏障中的一类:读写屏障(用于强制读取或刷新主存的数据,保证数据一致性)Store屏障:当一个线程修改了volatile变量的值,它会在修改后插入一个写屏障,告诉处理器在写屏障之前将所有存储在缓存中的数据同步到主内存。Load屏障:当另一个线程读取volatile变量的值,它会在读取前插入一个读屏障,告诉处理器在读屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果。
  对上面的例子使用javap查看JVM指令时,如果被volatile修饰时多一个ACCVOLATILE,JVM把字节码生成机器码时会在相应位置插入内存屏障指令,因此可以通过读写屏障实现volatile修饰变量的可见性。
  注意读写屏障的特点:可以将所有变量(包括不被volatile修饰的变量)一起全部刷入主存,尽管这个特性可以使未被volatile修饰的变量也具备所谓的可见性,但是不应该过于依赖这个特性,在编程时,对需要要求可见性的变量应当明确的用volatile修饰(当然除了volatile,synchronized、final以及各种锁都可以实现可见性,这里不过多说明)。2。volatile如何实现有序性?
  有序性是什么?
  有序性指禁止指令重排序,即保证程序执行代码的顺序与编写程序的顺序一致(程序执行顺序按照代码的先后顺序执行)。
  为什么会发生指令重排序?
  现代计算机为了能让指令的执行尽可能的同时运行起来,采用指令流水线的方式,若指令之间不具有依赖,可以使流水线的并行最大化,所以CPU对无依赖的指令可以乱序执行,这样可以提高流水线的运行效率,在不影响最后结果的情况下,Java编译器可以通过指令重排序来优化性能。
  编译器和处理器常常会对指令做重排序,一般分为三种类型:编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。指令级并行重排序:现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。内存系统重排序:由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
  所以指令重排序是指编译器和处理器为了优化程序的性能,在不改变数据依赖性的情况下,调整指令的执行顺序。
  这种优化在单线程情况下没有问题,但是在多线程情况下可能会导致影响程序结果。接下来将介绍一个多线程下指令重排的例子。2。1有序性例子
  这里以单例模式的常用实现方式DLC双重检查为例子volatile有序性例子author单程车票publicclassSingleton{使用volatile进行修饰privatestaticvolatileSingletoninstance;私有化构造器privateSingleton(){}双重检查锁publicstaticSingletongetInstance(){if(instancenull){synchronized(Singleton。class){if(instancenull){instancenewSingleton();}}}returninstance;}}
  如果写过单例模式的双重锁检查实现方式,会发现声明的变量被volatile修饰,那么为什么这里需要使用volatile修饰呢?
  第一个原因是可见性,如果没有volatile修饰的话,当一个线程给instance赋值即instancenewSingleton();后,其他线程如果无法及时看到instance更新,会导致创建多个单例对象,这样就不符合单例模式设计思想了,所以需要使用volatile修饰。
  第二个原因则是禁止指令重排序(保证有序性),为什么需要禁止指令重排呢?
  首先需要了解实例一个对象可以分为三个步骤:分配内存空间初始化对象将对象引用赋值给变量
  由于指令可以进行重排序,所以步骤可能发生变化变为分配内存空间将对象引用赋值给变量初始化对象
  如果未使用volatile修饰变量的话,多线程情况下可能出现这样的情况:
  一个线程在执行第二步(将对象引用赋值给变量,即此时变量不为null)时,而另一个线程进入第一次非空检查,此时发现变量不为null,直接返回对象,但是此时的对象由于指令重排序的原因并未进行初始化,即返回了一个未初始化的对象。将一个未初始化的变量暴露出来会导致不可预料的后果。
  所以需要volatile保证变量有序性,禁止指令重排序。2。2volatile实现有序性的原理
  内存屏障的四种指令
  内存屏障中禁止指令重排序的内存屏障的四种指令
  指令
  说明
  LoadLoad屏障
  保证在该屏障之后的读操作,不会被重排序到该屏障之前的读操作
  StoreStore屏障
  保证在该屏障之后的写操作,不会被重排序到该屏障之前的写操作,并且该屏障之前的写操作已被刷入主存
  StoreLoad屏障
  保证在该屏障之后的读操作,能够看到该屏障之前的写操作对应变量的最新值
  LoadStore屏障
  保证在该屏障之后的写操作,不会被重排序到该屏障之前的读操作
  Java编译器会在生成指令时在适当位置插入内存屏障来禁止特定类型的处理器重排序。
  volatile的插入屏障策略在每个volatile写操作的前面插入一个StoreStore屏障在每个volatile写操作的后面插入一个StoreLoad屏障在每个volatile读操作的后面插入一个LoadLoad屏障在每个volatile读操作的后面插入一个LoadStore屏障
  即在每个volatile写操作前后分别插入内存屏障,在每个volatile读操作后插入两个内存屏障。
  如何通过内存屏障保持有序性?
  分析上面的双重检查锁例子:
  不加volatile修饰时,多线程下可能出现的情况是这样的:
  为了避免这种情况,使用volatile修饰变量时,会插入内存屏障双重检查锁publicstaticSingletongetInstance(){if(instancenull){第一次检查synchronized(Singleton。class){加锁if(instancenull){第二次检查插入StorStore屏障插入屏障禁止下面的new操作和读取操作重排序instancenewSingleton();创建对象插入LoadLoad屏障插入屏障禁止下面的读取操作和上面的new操作重排序}}}returninstance;}
  这里使用volatile修饰变量并不能避免实例对象的三个步骤重排序,因为volatile关键只能避免多个线程之间的重排序,不能避免单个线程内部的重排序。
  这里volatile保证有序性的作用在于插入屏障之后必须等创建对象完成后才能进行读取操作,也就是说需要线程1的创建对象整个步骤完成后才会让线程2进行读取,禁止了重排序,这样就避免了返回一个未初始化的对象,保证了有序性。3。volatile为什么不能保证原子性?
  什么是原子性?
  原子性指一个操作或一系列操作是不可分割的,要么全部执行成功,要么全部不执行(中途不可被中断)。
  为什么volatile不能保证原子性呢?
  通过一个例子来证明volatile不能保证原子性原子性例子author单程车票publicclassAtomicityDemo{使用volatile修饰变量publicstaticvolatileinti0;publicstaticvoidmain(String〔〕args){ExecutorServicepoolExecutors。newFixedThreadPool(1000);多线程情况下执行1000次for(intj0;j1000;j){pool。execute(()i);}打印结果System。out。println(i);pool。shutdown();}}输出结果:997
  正常情况下,打印结果应该为1000,但是这里却是997,说明这段程序并不是线程安全的,可以看出volatile无法保证原子性。
  准确来说应该是volatile无法保证复合操作的原子性,但能保证单个操作的原子性。
  这里volatile保证单个操作的原子性可以应用于使用volatile修饰共享的long或者double变量(可以避免字分裂情况,具体想要了解到可以查阅相关资料这里不做过多说明)。
  i操作是原子操作吗?
  i其实不是原子操作,实际上i分为三个步骤:读取i的值将i自增1(i1)写回i的新值(ii1)
  这三个步骤每一步都是原子操作,但是组合起来就不是原子操作了,在多线程情况下同时执行i,会出现数据不一致性的问题。
  所以可以证明volatile修饰的变量无法保证原子性。
  可以通过AtomicInteger或者synchronized来保证i的原子性。4。volatile常见的应用场景?4。1状态标志位
  使用volatile修饰一个变量通过赋值不同的常数或值来标识不同的状态。可以通过布尔值来控制线程的启动和停止publicclassMyThreadextendsThread{状态标志变量privatevolatilebooleanflagtrue;根据状态标志位来执行publicvoidrun(){while(flag){dosomething}}根据状态标志位来停止publicvoidstopThread(){flagfalse;改变状态标志变量}}4。2双重检查DLC
  在多线程编程下,一个对象可能会被多个线程同时访问和修改,而且这个对象可能会被重新创建或者赋值为另一个对象。此时可以通过volatile来修饰该变量,保证该变量的可见性和有序性。
  就如单例模式的双重检查DLC可以通过volatile来修饰从存储单例模式对象的变量。单例模式的双重检查方式publicclassSingleton{使用volatile进行修饰privatestaticvolatileSingletoninstance;私有化构造器privateSingleton(){}双重检查锁publicstaticSingletongetInstance(){if(instancenull){synchronized(Singleton。class){if(instancenull){instancenewSingleton();}}}returninstance;}}4。3较低开销的读写锁
  使用volatile结合synchronized实现较低开销的读写锁,由于volatile可以保证变量的可见性和有序性,而synchronized可以保证变量的原子性和互斥性,可以结合使用实现较低开销的读写锁。读写锁实现多线程下的计数器publicclassVolatileSynchronizedCounter{volatile变量privatevolatileintcount0;synchronized方法publicsynchronizedvoidincrement(){count;原子操作}publicintgetCount(){returncount;}}
  使用volatile修饰变量,synchronized修饰方法,这样volatile修饰变量具有可见性,写操作会被其他线程立刻可见,synchronized修饰方法保证count操作的原子性和互斥性,这样实现的读写锁,读操作无锁,写操作有锁,降低了开销。

除了跑腿兼职,还有什么兼职适合晚上七点以后做。最好有利于交际?有句话我不知道你听过没有如果你在工作中交不到朋友是很正常的如果在工作中交到朋友那是你的幸运因为工作大多都有利益关系公务员的攀比心更严重,更交不到什么朋友。所谓人走茶凉这句话在哪里都农民工家庭收入一年10多万,遇事却拿不出3万钱!钱哪去了?1。首先我要说,农民工家庭年收入十多万不多。做为一个家庭,孩子读书,过年过节,三病两痛,人情来往,日常生活,除去家庭开支,一年也留不下多少钱。2。目前的农村,家庭最大的开支是在城里有些小学老师说绩效工资是拿自己的钱来请领导罚自己,对此你怎么看?这个我有发言权。我是一名农村教师,从教十三年,中一职称,现在的工资是三千八百二十三块,被扣除了九百八十二块作为绩效工资。一个星期十节课,早上六点多出发到学校打卡,直到下午四点四十分退休工资只有1300元,还要继续工作吗?这件事情,还是要看家庭和自己的身体情况。我母亲一直也没有正式工作,是通过缴纳灵活就业人员保险参加的社保。2012年退休以后,养老金只有600多元,她的选择是出去工作。在一个叔叔的帮你知道仙人指路吗?一旦K线上出现仙人指路,后市该如何操作?在股价低位区,出现了长上影线的K线,这长上影K钱就叫做仙人指路。出现仙人指路,后面还有一句指到哪里打到哪里。也就是说,在低位区出现了仙人指路后,可等待股价调整到位后介入,目标管理仙在家乡上班3000工资,12小时一天能照看点家庭,去广东9000工资,怎么选?如果你的孩子小,需要人照顾,就带爸妈孩子一起去广东,如果孩子大了,可以自已去上学,自理了,就带孩子去广东打工。爸妈年纪大一般都喜欢呆在自己家中,趁着父母还不需要照顾,多搛点钱。等双偏心的父母到底有多可怕?77岁的曹北义住养老院,四个女儿不愿负担费用,她却只找小女儿的麻烦!小女儿爸爸去世,你不通知我!十几万的遗产,分我1000块!我就是个多余的人,你告我去吧!为了维护自己的合法权益,买彩票中的奖金为什么会花得非常快?这个是一个很简单的道理啊!钱来得容易,花的也快啊!反之,如果是你辛辛苦苦一滴汗水摔八瓣赚来的,你一定会珍惜不舍得乱花啊!彩票中奖熟悉意外之财,中国有个说法是德和财要配位。财和你的德应届二本毕业生参军入伍有什么利弊?首先对你立志参军,献身国防的想法向你致敬!你是应届二本生,虽不是一本生,但在学历上属本科生。一,作为本科生,如果你参军入伍,恭喜你赶上了一个好时代,在这个亲睐知识,尊重的人才的年代低热量食物吃到饱,算是节食吗?广义的节食包含两个方面的内容1是少吃。少吃比较明确,就是像小鸡啄食一样只吃一点点。从热量的角度就是每天摄入的热量低于自己的基础代谢率。中国女生的基础代谢率大概是1200千卡,男生的前列腺切除后还会长回来吗?前列腺切除的适应症主要是有两类疾病前列腺癌和前列腺增生。前列腺是男性特有的泌尿生殖腺体,而近年来由于我国人口老龄化的加重和青年人群生活压力日益增加,使得前列腺疾病在临床上也是明显的
土耳其8级地震威力有多大?历史上有王朝直接被颠覆一天两场7。8级地震,震感相当于130颗原子弹爆炸,土耳其遭遇百年最强烈地震!地震造成房屋坍塌罕见双震北京时间2月6日9时17分,土耳其发生7。8级地震,震源深度20千米。12小时世界上最美好的故事,是相遇!怡耕园棉麻服饰01相遇,是一场不期而遇,缘起缘灭,温柔而缓慢的旅程。有时,它只是一瞬间的邂逅,两个人相视一笑,彼此的心便暗暗的连接在一起。有时,它是一份深厚的情谊,从你我相遇的那一土耳其地震已5894死亡期货未遵守打击洗钱及恐怖分子资金筹集规定被罚泰国巴士司机短缺早餐每天早上3分钟,精选财经资讯国内01hr河南按每人不超过500元标准发消费券2月7日,河南省总工会印发关于做好大力提振市场信心促进经济稳定向好助力释放消费潜力工作的通知,通知明确,土耳其都地震成这样了,还出兵打击库尔德武装,为何如此执念?土耳其叙利亚地震遇难者超1。5万人截止2月8日,土耳其和叙利亚北部发生的大地震已造成超过1。5万人遇难,其中土耳其12391人遇难叙利亚2992人遇难,如此严重的地震来袭,此时此刻从广州前往土耳其救援的搜救犬名叫Lucky,盼带来好运土耳其连续发生两次7。8级地震,造成重大人员伤亡和财产损失,救援情况全球关注。2月9日,南方都市报N视频特派记者从广州飞抵土耳其伊斯坦布尔,将前往震中附近追踪报道。南都记者注意到,非正式会谈嘉宾唐小强土耳其失联13年前来中国留学,曾路遇车祸停车救人欧玛omar发文称唐小强被埋废墟下红星新闻记者丨陈卿媛罗梦婕责编丨邓旆光编辑丨张莉据新华社消息,截至北京时间2月7日11时,6日凌晨发生在土耳其南部靠近叙边境地区的强烈地震已致两国强震多发的土耳其共和国地理构造特殊性有哪些?提到土耳其,你的第一反应是什么?是北约成员国,是奥斯曼帝国不完全继承者,还是强震多发的国度?土耳其星月红旗飘扬1299年奥斯曼一世建立奥斯曼帝国。一战结束,奥斯曼帝国解体。土耳其在继续李唐皇族子嗣生育情况研究上篇我统计了唐太祖李虎至唐睿宗李旦的子嗣生育情况,本篇继续第二阶段的统计与研究。自唐玄宗李隆基至唐宪宗李纯的生育情况。一唐玄宗李隆基生三十子(1)共七子早夭,分别是第七子十子十一子20世纪以来世界上已经消亡了的国家有哪些随着时间的推移,世界的格局也在悄悄变化。那些曾经的辉煌现在已经成为历史,下面湖湘生活百科就给大家分享一下20世纪以来世界上已经消亡了的国家,感兴趣的朋友们一起来了解一下吧。一20世太平天国之拜上帝教的起源拜上帝教的起源洪秀全于1814年出生于广东花县西北福源村,15岁科考进入县试,但之后的多次科考中院试落榜,至31仍为童生。在广州参加院试时,偶然得到了一本外国传教士的书籍,深受触动1962年,对印反击时,解放军牺牲的最高级军官是何军衔与职务?1932年3月,一支红军师部队创建。在穿过90年的岁月后,11师战友李小彤和她的团队趁着该红军师创建90周年之际,于山南烈士陵园探望早年在对印自卫反击战中牺牲的烈士们。其中有一位叫
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网