volatile主要是实现了线程之间的变量的共享可见
为了提高访问效率,处理器不会直接和系统内存做通信,而是会通过高速缓存行和系统内存做通信。缓存行是高速内存中的最小存储单位,每次读写操作都是对一整行缓存行进行读写。
写操作时,处理器会把数据写到内存缓存的区域中,它会首先检查这个区域的内存地址是否存在于高速缓存行中,如果存在的话,则将这块内存缓存更新到高速缓存中(不会更新到系统内存中),如果高速缓存行中不存在,则更新到系统内存中。
当处理器识别到读写的内存是可缓存的,则处理器会将整个缓存行(是整个缓存行)读到高速缓存中(如L1/L2/L3等)
1:更新volatile变量时,jvm会像处理器发送一条LOCK指令,该指令会通知CPU处理器把高速缓存中对应缓存行的值通过总线更新到系统内存。
2:每个CPU处理器都会通过嗅探方式检查其它处理器打算写内存地址操作,一旦发现写内存地址当前出于共享状态,则对应的处理器则会使自己内部指向该共享地址的高速缓存行失效。
当变量定义成volatile时候,对这个变量进行操作,
jvm会向处理器发送一条
Lock前缀
的指令。在多处理器环境中,
LOCK#信号(和lock前缀指令是两个概念)确保在声言该信号期间,处理器可以
独占任何共享内存(它是通过总线锁锁定CPU和内存的通道。这里不是高速缓存行,高速缓存行是属于处理器内部的,不同处理器是不一样的)。但是,在最近的处理器里。LOCK信号一般不会锁总线,而是锁缓存,毕竟缓存的总开销比较大。
如果访问的内存区域已经缓存在处理器内部,则不会声言
LOCK#信号
信号(注意还是处于在接收到lock前缀的指令时),它只会锁定这块内存区域的缓存并写到系统内存里(也就是修改内部的内存地址),并使用缓存一致性机制来确保修改的原子性(缓存一致性可以确保阻止同时修改有多个处理器共享的缓存的内存区域)。当一个处理器的缓存回写到内存会导致其他处理器的缓存无效。每个处理器都会用嗅探来检测其它处理器打算写内存地址,当发现写的内存地址是共享内存地址,那么正在嗅探的处理器会使它自己的缓存行失效。
为什么追加64字节可以提高并发编程的效率?因为现代的机器的处理器的L1/L2/L3缓存的高速缓存行都是64个字节宽。而CPU处理器在执行LOCK操作的时候,会独占共享内存,导致别的CPU处理器就无法获取这个内存。所以当有一个高速缓存行里有两个信息A和B(因为一个信息填不满高速缓存行),如果这个时候刚好一个CPU1因为要将缓存行中A信息写到内存里,所以锁定了高速缓存行,而这个时候CPU2要把缓存行中的B信息写到内存里,由于此时高速缓存行被CPU1锁定,所以CPU2只能等待,所以并发效率就差点。