CAS 和ABA 问题

CAS 和ABA 问题

CAS:compare and swap

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

1、对CAS的理解:CAS是一种无锁算法。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

CAS比较与交换的伪代码可以表示为:

````

do{

备份旧数据;

基于旧数据构造新数据;

}while(!CAS( 内存地址,备份的旧数据,新数据 ))

````

ABA 问题:

就是说一个线程把数据A变为了B,然后又重新变成了A。此时另外一个线程读取的时候,发现A没有变化,就误以为是原来的那个A。这就是有名的ABA问题。

ABA问题会带来什么后果呢?

比如说:

你有一个小女朋友,一年前她甩了你,后面跟了高富帅,昨天她给你打电话要复合,你对此一无所知,以为她还是原来的那个她。

显然这个女朋友已经不是原来那个她了,她是个出过轨的女人,伤害过你,。。。

如何解决?用额外的一个版本号保存当前状态就好。

每次修改了这个值的时候,版本号++,可以通过版本号判断当前变量是否被修改过


之前说它是一种乐观锁,现在又说是无锁算法,可能有点晕,其实乐观锁并不是真的锁,只是一种概念,或者说一个机制,先来了解一下乐观锁和悲观锁。

乐观锁& 悲观锁

两种锁只是一种概念,并不是真正的锁。

乐观锁:乐观锁认为一个线程去拿数据的时候不会有其他线程对数据进行更改,所以不会上锁。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

实现方式:CAS机制、版本号机制

悲观锁:悲观锁认为一个线程去拿数据时一定会有其他线程对数据进行更改。所以一个线程在拿数据的时候都会顺便加锁,这样别的线程此时想拿这个数据就会阻塞。比如Java里面的synchronized关键字的实现就是悲观锁,悲观锁适合写操作非常多的场景。

实现方式:就是加锁。


举例一个典型的实现:AtomicInteger

AtomicInter 有一个方法 incrementAndGet,这个方法就是用CAS实现





AtomicInteger 的incrementAndGet 就是cas的一个典型实现




getAndAddInt 这个方法实际上就是 前面伪代码的实现



实际上是调用的native方法

跟踪到最底层参考JDK的源码(参考马老师视频),实际上是用汇编语言解决的,跟踪到最后调用的就是这个方法了


asm表示这是汇编语言

lock_IF_MP 表示如果存在多个CPU,则会把它锁起来

lock_cmpxchg 这条指令就是真正的实现,前面的lock 保证了原子性,意思是当执行cmpxchg 时,其他CPU不允许对里面的值做修改。,cmpxchg实际上是cas操作在硬件级别对应的一个指令。


参考:https://www.jianshu.com/p/21be831e851e

你可能感兴趣的:(CAS 和ABA 问题)