CAS:compar and swap,是一种自旋锁

image-20210826103838080

属于乐观锁,是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

ABA问题

加入有三个线程,线程A读取内存中数据为0,然后i++,线程B读取内存中数据位0,然后i++,线程A运行比较慢,线程B把1写入内存,然后有一个线程C,读取内存中数据位1,进行i–操作,然后把0写入内存,这个时候线程A才运行完毕,然后读取内存中数据位0,认为没有发生修改,则把1写入数据。

这个时候线程A认为内存中0的数据没有修改,写入内存了,但是这个数据确实发生了修改。这就是ABA问题

解决方案:

  1. 不管
    • 这个一个值,我不在在乎,你想改就改,我要的是最终的值
  2. 添加版本
    • 这是一个引用,应用的地址没有发生改变,但是这个地址指向的对象内容可能已经改变了,这个时候添加版本号解决
    • 在值上添加一个版本号,(可以是线程id)在比较的时候会比较值和版本号,如果这两个都一样,则会赋值

CAS在java中的底层实现是native,是用的C++实现的(下面为linuxX86操作系统的实现)

  1. Unsafe_CompareAnSwapINT

  2. 调用Atomic的cmpxchg

  3. 调用汇编码__asm__

  4. 调用cmpxchg1,(如果是多核cpu执行这个方法会添加lock)

  5. 最终实现: lock cmpxchg 指令

    在多核cpu情况下,如果没有加锁,执行还不是原子性的,有可能被其他核干扰,所以添加了lock解决,总线嗅探(Bus Snooping)技术。这个策略总线嗅探,本质上就是把所有的读写请求都通过总线(Bus)广播给所有的CPU核心,然后让各个核心去“嗅探”这些请求,再根据本地的情况进行响应。

synchronized和violate的底层实现都是lock cmpxchg 指令

硬件:lock指定在执行后面指令的时候锁定一个北桥信号(不采用锁总线的方式)

推荐文章:

cpu底层cas原理:https://blog.csdn.net/weixin_30691871/article/details/100004896