笔记,读《Java并发编程的艺术》的笔记。本文中除了读书笔记之外,里面还夹杂着一些自己的理解,可能有理解不到位的地方
volatile
是轻量级的synchronized
,它在多处理器开发中保证共享变量的可见性
。可见性
是当一个线程修改一个共享变量时,其他线程可以读到这个修改的值。volatile
不会引起线程上下文的切换和调度。
CPU的术语定义
内存屏障: 是处理一组处理器指令,用于实现对内存操作的顺序限制。
缓冲行: 缓存中可以分配的最小存储单位。
原子操作: 不可中断的一个或一系列操作。
缓存行填充: 当处理器识别到 从内存中读取数据操作数是可缓存的,处理器读取整个缓存行 到适当的缓存。
缓存命中: 如果进行高速 缓存行填充 操作的内存位置仍然是下一次处理器访问的地址时,处理器在缓存中读取操作数,而不是在内存中。
写命中: 如果处理器将操作数写回到一个内存缓存的区域是,它首先会检查这个缓存的内存地址是否在缓存行中,如果存在一个有效的缓存行,则处理器将这个操作数写回到缓存中,而不是内存中。
写缺失: 一个有效的缓存行被写入到不存在的内存区域。
Lock前缀指令:
在volatile
修饰的共享变量的写操纵中,存在一个以Lock
为前缀的指令,这个指令会触发两件事:
- 将当前处理器缓存行的数据写回到系统内存中。
- 这个写回内存的操作会使其他CPU里缓存了该内存地址的数据无效。
这两件事保证了 volatile
修饰的共享变量保持 可见性。
具体操作步骤:
volatile修饰的变量进行写操作,JVM会向处理器发送一个LOCK前缀的指令,此指令会导致声言处理器的LOCK#信号,此信号可以让处理器锁住总线,不让其他CPU访问内存。目前的处理器不会锁住总线,只会锁住内存区域中缓存并写到内存中。利用缓存一致机制来确保原子性。(缓存一致性机制会阻止同时修改,由两个以上处理器缓存的内存区域数据)。
通过嗅探技术,来检测其他处理器打算写内存地址,而这个地址又是共享状态,那么正在嗅探的处理器会将它的缓存行无效,下次访问,强制缓存行填充。
volatile 优化
通过追加字节的方式 进行优化。使其总的字节数 正好等于 缓存行的大小。因为在修改时,处理器会将整个缓存行锁住,也就是说,如果一个缓存行是由多个部分组成时,修改其中一部分,会导致缓存行中的其他部分也锁住。
所以在变量不会被频繁地写时,需要通过追加字节的方式进行优化。