并发编程无处不在,并发提供了高性能,但是也带来了复杂性。java并发编程中有一个重要的关键字volatile,volatile关键字解决了并发编程中可见性和有序性问题,那么volatile是如果实现的呢,让我们一起来深入了解一下。
我们先从了解volatile的定义开始
volatile的定义
Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排它锁单独获取这个变量。Java提供了volatile,在某些情况下比锁更加方便。
我们都知道java虚拟机中是有内存模型的(JMM),用来兼容各种硬件和操作系统的内存访问差异。硬件上cpu的运算速度比内存块的多,所以cpu上又有多层高速缓存。
java线程的工作内存对应cpu的高速缓存,java主内存对应硬件的内存。
volatile是如何保证可见性呢?
增加了volatile的变量在编译器编译后会生成一个lock指令。
比如:
public volatile Singleton instance = new Singleton();
编译后
0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);
lock前缀的指令在多核处理器下引发两件事情
- 将当前处理器缓存行的数据写回到系统内存。也就是说,将java线程的局部变量立即写回到主内存
- 写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。也就是说,java线程再次读取已经更改了数据的局部变量会重新从主内存读取
下面我们来分别介绍上面的两件事情
第一条:当前处理器也就是线程的数据是怎么写回到系统内存的?lock指令确保在该声明期间,处理器可以独占任何共享内存。可以通过锁总线或者锁缓存来实现,不同的cpu型号,处理方式不同,锁缓存的开销会比锁总线要小很多。独占共享内存后将数据写回到系统内存,也就是java的主内存中。并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”,缓存一致性机制会阻止同事修改两个以上处理器缓存的内存区域数据。
第二条:其他处理器(其他线程)是如何感知变量缓存过期从而重新读取?处理器使用MESI(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致性。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存数据在总线上保持一致性。例如,如果通过嗅探一个处理器来检查其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器使它的缓存无效,在下次访问相同内存地址时,强制执行缓存行填充,也就是重新读取。
好了volatile是如何实现可见性操作就是这些。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。