Java并发系列之volatile

讲到Java并发,多线程编程,一定避免不了对关键字volatile的了解,那么如何来认识volatile,从哪些方面来了解它会比较合适呢?

个人认为,既然是多线程编程,那我们在平常的学习中,工作中,大部分都接触到的就是线程安全的概念。

而线程安全就会涉及到共享变量的概念,所以首先,我们得弄清楚共享变量是什么,且处理器和内存间的数据交互机制是如何导致共享变量变得不安全。

共享变量

能够在多个线程间被多个线程都访问到的变量,我们称之为共享变量。共享变量包括所有的实例变量,静态变量和数组元素。他们都被存放在堆内存中。

处理器与内存的通信机制

大家都知道处理器是用来做计算的,且速度是非常快的,而内存是用来存储数据的,且其访问速度相比处理器来说,是慢了好几个级别的。那么当处理器需要处理数据时,如果每次都直接从内存拿数据的话,就会导致效率非常低,因此在现代计算机系统中,处理器是不直接跟内存通信的,而是在处理器和内存之间设置了多个缓存,也就是我们常常听到的L1, L2, L3等高速缓存。

具体架构如下所示:


memory_processor_communication.png

处理器都是将数据从内存读到自己内部的缓存中,然后在缓存中对数据进行修改等操作,结束后再由缓存写到回主存中去。
如果一个共享变量 X,在多线程的情况下,同时被多个处理器读到各自的缓存中去,当其中一个处理器修改了X的值,改成Y了,先写回了内存,而此时另外一个处理器,又将X改成Z,再写回内存,那么之前的Y就会被覆盖掉了。

这种情况下,数据就已经有问题了,这种因为多线程操作而导致的异常问题,通常我们就叫做线程不安全

memory_processor_communication_core1.png

memory_processor_communication_core2.png

如上述两图所示,X的变量同时被不同的处理器修改成各自的Y和Z,那么如何避免这种情况呢?
这就涉及到了Java内存模型中的可见性的概念。

Java内存模型之可见性

可见性,意思就是说,在多线程编程中,某个共享变量在其中一个线程被修改了,其修改结果要马上能够被其他线程看到,拿上面的例子来说,也就是当X在其中一个处理器的缓存中被修改成Y了, 另一个处理器必须能够马上知道自己缓存中的X已经被修改成Y了,当此处理器要拿此变量去参与计算的时候,必须重新去内存中将此变量的值Y读到缓存中。

而一个变量,如果被声明成violate,那么其就能保证这种可见性,这就是volatile变量的作用了。

volatile

那么 volatile 变量能够保证可见性的实现原理是什么?
声明成volatile的变量,在编译成汇编指令的时候,会多出以下一行:

0x0bca13ae:lock addl $0x0,(%esp)      ;

这一句指令的意思是在寄存器上做一个+0的空操作,但这条指令有个Lock前缀。
而处理器在处理Lock前缀指令时,其实是声言了处理器的Lock#信号。
在之前的处理器中,Lock#信号会导致传输数据的总线被锁定,其他处理器都不能访问总线,从而保证处理Lock指令的处理器能够独享操作数据所在的内存区域。

但由于总线被锁住,其他的处理器都被堵住了,影响多处理器执行的效率。在后来的处理器中,声言Lock#信号的处理器,不会再锁住总线,而是检查到数据所在的内存区域,如果是在处理器的内部缓存中,则会锁定此缓存区域,将缓存写回到内存当中,并利用缓存一致性的原则来保证其他处理器中的缓存区域数据的一致性。

缓存一致性

缓存一致性原则会保证一个在缓存中的数据被修改了,会保证其他缓存了此数据的处理器中的缓存失效,从而让处理器重新去内存中读取最新修改后的数据。

在实际的处理器操作中,各个处理器会一直在总线上嗅探其内部缓存区域中的内存地址在其它处理器的操作情况,一旦嗅探到某处理器打算修改某内存地址,而此内存地址刚好也在自己内部的缓存中,则会强制让自己的缓存无效。当下次访问此内存地址的时候,则重新从内存当中读取新数据。

volatile不仅保证了共享变量在多线程间的可见性,其还保证了一定的有序性。

有序性

何谓有序性呢?
事实上,java程序代码在编译器阶段和处理器执行阶段,为了优化执行的效率,有可能会对指令进行重排序。
如果一些指令彼此之间互相不影响,那么就有可能不按照代码顺序执行,比如后面的代码先执行,而之前的代码则慢执行,但处理器会保证结束时的输出结果是一致的。
以上的这种情况就说明指令有可能不是有序的。

volatile变量,上面我们看过其汇编指令,会多出一条Lock前缀的指令,这条指令能够 保证,在这条指令之前的所有指令全部执行完毕,而在这条指令之后的所有指令全部未执行,也相于在这里立起了一道栅栏,称之为内存栅栏,而更通俗的说法,则是内存屏障

那么有了这道屏障,volatile变量就禁止了指令的重排序,从而保证了指令执行的有序性。

所有对volatile变量的读操作一定发生在对volatile变量的写操作之后。这同时也说明了volatile变量在多个线程之间能够实现可见性的原理。所以各种规定和操作,其实之间互有关联,彼此依赖,才能更好地保证指令执行的准确和效率。

内存屏障

在上面我们也引出了内存屏障的概念,也知道了,其实它就是一组处理器的操作指令。

插入一个内存屏障,则相当于告诉处理器和编译器先于这个指令的必须先执行,后于这个指令的必须后执行。

image

内存屏障另一个作用是强制更新一次不同CPU的缓存。

例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。

这再仔细一想,不就是上面所说的volatile的作用吗?

所以,内存屏障,可见性,有序性,缓存一致性原则,在java并发中各种各样的名词,本质上可能就只是同一种现象或者同一种设计,从不同的角度观察和探讨所得出的不同的解释。
下一篇文章
Java并发系列之synchronized

参考资料

聊聊并发(一)深入分析Volatile的实现原理

内存屏障与JVM并发

Java并发编程:volatile关键字解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容