CAS锁了总线或者缓存行还是volatile锁了总线或者缓存行?
CAS全称Compare And Set(或Compare And Swap),CAS包含三个操作数:内存位置(V)、原值(A)、新值(B)。简单来说CAS操作就是一个虚拟机实现的原子操作,这个原子操作的功能就是将旧值(A)替换为新值(B),如果旧值(A)未被改变,则替换成功,如果旧值(A)已经被改变则替换失败。
Java的volatile其实是通过汇编指令,加上lock前缀进行底层处理的,在CPU的底层其实是不存在volatile这种概念的。
早期的CPU,总是采用锁总线的方式进行加锁。具体方式是:一旦遇到lock指令,就由仲裁器选择一个核心独占总线。其余的核心不能在通过总线与内存进行通讯。从而达到“原子性”的目的。这中方式虽然解决了线程之间的可见性,但是却非常的低效,为了原子性结果搞的其他的CPU就不能工作了。因此,从intel P6 CPU开始,另一种方式出现了,改用Ringbus + MESI 协议,这种技术被Intel成为”Cache Locking“.
Intel P6是Intel第6代架构的CPU,其实也很老了,差不多1995年出的…… 比如Pentium Pro,Pentium II,Pentium III都隶属于P6架构
MESI大致的意思是:若干个CPU核心通过ringbus连到一起。每个核心都维护自己的Cache的状态。如果对于同一份内存数据在多个核里都有cache,则状态都为S(shared)。一旦有一核心改了这个数据(状态变成了M),其他核心就能瞬间通过ringbus感知到这个修改,从而把自己的cache状态变成I(Invalid),并且从标记为M的cache中读过来。同时,这个数据会被原子的写回到主存。最终,cache的状态又会变为S。
CAS在x86汇编的大概指令是:
lock cmpxchg a, b, c
AS指令在Intel CPU上称为CMPXCHG指令,它的作用是将指定内存地址的内容与所给的某个值相比,如果相等,则将其内容替换为指令中提供的新值,如果不相等,则更新失败。
关于CAS和ABA。
实际上CAS和cmpxchg压根就没处理过ABA问题。严格来说CAS就不会有ABA的问题,它只是一个简单的,原子的"比较-设置值"的指令而已。
var curVal = getValue();
var newVal = modify(curVal);
compareAndSwap(getValueAddr(), curVal, newVal); // 这里是CAS
即这个代码的第一句和第三句可能看到的curVal是一样的,但是有可能造这个curVal在另一个线程ABA了。
如果真的需要解决ABA问题,需要上层代码来处理,比如
把value和version放到一起形成一个变量的值(比如 "62@v1“),然后对这个变量的值做CAS。这种比较适合值本身比较简单的场景。
把value和version包一个对象,然后对对象的引用做CAS(Java的AtomicStampedReference就是这么干的)。
CAS策略有如下需要注意的事项:
在线程抢占资源特别频繁的时候(相对于CPU执行效率而言),会造成长时间的自旋,耗费CPU性能。
链接:https://www.zhihu.com/question/65372648/answer/415311977
网友评论