Java并发编程(02 下)——细说可见性问题


前文说到,为了平衡CPU和内存之间的速度差异而增加了缓存,在多核场景下CPU操作完不知道何时会写到内存从而引出了可见性的问题。在Java1.5版本里使用happens-before规则对volatile语义进行了增强,进一步保证了可见性。

1. volatile的定义与实现原理

Java编程语言允许线程访问共享变量,为了
确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。如果一个字段被声明成volatile,Java线程内存
模型确保所有线程看到这个变量的值是一致的。

1.1volatile是如何来保证可见性的呢

X86处理器下通过工具获取JIT编译器生成的汇编指令来查看对volatile进行写操作时,

// java代码, instance是volatile变量
instance = new Singleton(); 

// 转成汇编代码
0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);

我们会发现在有volatile变量修饰的共享变量的汇编代码中有个lock前缀的指令。它会让处理器:

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

缓存行cache line,缓存中可以分配的最小存储单位。处理器填写缓存线时会加载整个缓存线,需要使用多个内存读周期。

  1. 写回内存的操作会使其他CPU里缓存了该内存地址的数据无效

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

在这里插入图片描述
一个设备占用那条总线之后,其他设备无法在那条总线进行数据传输。

MESI: modified-exclusive-shared-invaild
缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。

锁的底层其实也是反复操作一个volatile变量,而多线程的其他操作也是基于volatile的特性实现的

1.2 volatile的实现

  • lock前缀指令会引起处理器缓存回写到内存
    lock前缀指令导致在执行指令期间声言处理器LOCK#信号,在声言该信号期间,处理器可以独占任何共享内存。有的处理器里,由于锁总线的开销比较大故LOCK#信号不锁总线,而是锁缓存。
  • 处理器的缓存回写到内存会导致其他处理器的缓存无效
    在多核处理器系统中进行操作的时候,处理器能嗅探其他处理器访问系统内存和他们的内部缓存。处理器使用嗅探技术保证它的内部缓存,系统内存和其他处理器的缓存的数据在总线上保持一致。如果通过嗅探一个处理器来检测其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充。

缓存行填充cache line fill,当处理器识别到内存中读取操作数是可缓存的,处理器读取整个缓存行到适当的缓存(L1, L2,L3或所有)

2.volatile的使用优化

一个变量引用占4个字节,追加15个变量占60个字节,再加上父类的value变量,一共64字节。用这样的方式来填满高速缓冲区的缓存行,避免头节点和尾节点加载到同一缓存行,使得头尾节点在修改时不会相互锁定。

处理器的L1、L2或L3缓存的高速缓存行是64个字节宽,不支持部分填充缓存行,这意味着,如果队列的头节点和尾节点都不足64字节的话,处理器会将它们都读到同一个高速缓存行中,在多处理器下每个处理器都会缓存同样的头、尾节点,当一个处理器试图修改头节点时,会将整个缓存行锁定,那么在缓存一致性机制的作用下,会导致其他处理器不能访问自己高速缓存中的尾节点,而队列的入队和出队操作则需要不停修改头节点和尾节点,所以在多处理器的情况下将会严重影响到队列的入队和出队效率。

并不是都要在使用volatile变量时都应该追加到64字节:在两种场景下不应该
使用这种方式。

  • 缓存行非64字节宽的处理器。
  • 共享变量不hi被频繁的写。

不过这种追加字节的方式在Java 7以上可能不生效,因为Java变得更加智慧,它会淘汰或重新排列无用字段,需要使用其他追加字节的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值