原子和原子操作
- 原子(atomic)本意是“不能被进一步分割的最小粒子”,
- 原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。
处理器实现原子操作
总线索
所
谓总线锁
就是使用
处
理器提供的一个LOCK#信号,当一个
处
理器在
总线
上
输
出此信号
时
,其他
处
理器的
请
求将被阻塞住,那么
该处理器可以独占共享内存。
缓存锁
由于总线索开销较大,所以有些情况下使用缓存锁来对总线索进行一个优化。
所
谓
“
缓
存
锁
定
”
是指内存区域如果被
缓
存在
处
理器的
缓
存行中,并且在Lock
操作期
间
被
锁
定,那么当它
执
行
锁
操作回写到内存
时
,
处
理器不在
总线
上声言LOCK
#信号,而是修改内部的内存地址,并允
许
它的
缓
存一致性机制来保
证
操作的原子性,因为缓
存一致性机制会阻止同
时
修改由两个以上
处
理器
缓
存的内存区域数据,当其他
处理器回写已被锁
定的
缓
存行的数据
时
,会使
缓
存行无效。
简单来讲就是,将内存区域的一部分缓存到缓存中并上锁,当你修改了这一部分数据后,其他处理器缓存中的这部分数据就失效了,这样保障了同一时间只有一个处理器可以对其进行操作。
不适用缓存锁的情况
- 当操作的数据不能完全缓存在处理器内部,或者操作的数据跨越多个缓存行(cache line)时,处理器会使用总线锁定(LOCK# 信号)
- 有些处理器不支持缓存锁定
Java如何实现原子操作
加锁
循环CAS
CAS实现原子操作的三大问题
ABA问题
- 可以通过加入版本号或者时间戳等机制来解决ABA问题,确保CAS操作的原子性和正确性。
循环时间长开销大
- 如果JVM能支持处理器提供的pause指令,那么效率会有一定的提升。pause指令有两个作用:第 一,它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零;第二,它可以避免在退出循环的时候因内存顺序冲突(Memory Order Violation)而引起CPU流水线被清空(CPU Pipeline Flush),从而提高CPU的执行效率。
只能保证一个共享变量的原子操作
- 将多个共享变量合并为一个共享变量来操作。