volatile:(保证即时可见性)

  • 保证可见性
  • 禁止重排序(无法保证临界区的重排序)
  • 无法保证原子性

volatile底层原理:

  • 修饰的变量会被加上 “ACC_VOLATILE” 标识
  • volatile修饰的变量,在执行时,底层的汇编指令,会加上一个lock前缀指令
  • lock前缀指令作用:触发硬件缓存锁定机制
    • 总线锁定
    • EMSI(缓存一致性协议)

JMM

JMM(Java内存模型 Java Memory Model, 简称JMM)
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在自己的工作内存中进行,读取主内存的值,到自己的的缓存区域(变量副本)

1645458670902-aacd0318-a0c4-42dc-bdbd-9de995854a69.png

EMSI缓存一致性协议 - 总线窥探(Bus Snooping)

窥探协议类型:

  • Write-invalidate

当处理器写入一个共享缓存块时,其他缓存中的所有共享副本都会通过总线窥探失效,MSI、EMSI更新

  • Write-update

当处理器写入一个共享缓存块时,其他缓存中的所有共享副本都会通过总线窥探更新

EMSI缓存一致性协议(Volatile 不支持原子性的原因)

image.png

  • 当两个线程,都执行写操作时,最终由 “总线裁决” (速度非常快)决定哪个线程抢占资源继续执行,另一个则失效
  • 缓存一致性协议的总线窥探什么情况会升级为总线锁?
    • 当缓存行存储的对象大于64byte的时候,就会升级为使用总线锁
    • 因为,两个缓存行之间无法保证原子操作

缓存行伪共享,什么是伪共享?

CPU缓存系统中是以缓存行(cache line)为单位存储的。目前主流的CPU Cache 的
Cache Line 大小都是64Bytes。在多线程情况下,如果需要修改“共享同一个缓存行的变
量”,就会无意中影响彼此的性能,这就是伪共享(False Sharing

举个例子: 现在有2个long 型变量 x 、y,如果有t1在访问x,t2在访问y,而x与y刚好在同一个
cache line中,此时t1先修改x,将导致y被刷新!

怎么解决伪共享?

Java8中新增了一个注解:@sun.misc.Contended。加上这个注解的类会自动补齐缓
存行,需要注意的是此注解默认是无效的,需要在jvm启动时设置才会生效。

总线锁定

  • 回到单核