Java中volatile关键字的解析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Hanoch_wang/article/details/90639897

本文是对《Java并发编程的艺术》一书中volatile部分的小结。

一、volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。

    “可见性”:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。volatile如果运用得当的话,它比synchronized的使用和执行成本更低,因为它不会引起上下文的切换和调度(上下文的切换是指CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务,但是,在切换之前会保存上一个任务的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换)

二、volatile的定义与实现原理

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。

                          

至于如何查看汇编指令,大家可以参考这篇文章(https://blog.csdn.net/wangming520liwei/article/details/81561922)

 有volatile变量修饰的共享变量进行读写操作时会多出第二行汇编代码,Lock前缀的指令在多核处理器下会引发两件事情:

     1)将当前处理器缓存行的数据写回到系统内存。

     2)这个写回内存的操作会使再其他CPU里缓存了该内存地址的数据无效

    为了提高速度,处理器不会直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完不知道会何时写回内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在的缓存行的数据写回到系统内存。但是,就算写回到系统内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议。

扫描二维码关注公众号,回复: 7628635 查看本文章

    所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内部将数据读到处理器缓存里。

三、规则实现的原理

    1)将当前处理器缓存行的数据写回到系统内存。Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号。在多处理器环境中,Lock#信号确保在声言该信号期间,处理器可以独享任何共享内存(因为它会锁住总线,导致其它CPU不能访问总线,不能访问总线就意味着不能访问系统内存)。但是,在最近的处理器中,LOCK#信号一般不锁总线,而是锁缓存,毕竟锁总线开销比较大。缓存一致性会阻止同时修改由两个以上处理器缓存的内存区域。

    2)这个写回内存的操作会使再其他CPU里缓存了该内存地址的数据无效。IA-32处理器和Intel64处理器使用MESI(修改、独占、共享、无效https://blog.csdn.net/muxiqingyang/article/details/6615199)控制协议去维护内部缓存和其他处理器缓存的一致性。如果通过嗅探一个处理器来检测其它处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器将使它的缓存行无效,下次再访问相同的内存地址时,强制执行缓存行填充。

 

猜你喜欢

转载自blog.csdn.net/Hanoch_wang/article/details/90639897