Java多线程: CAS

悲观锁与乐观锁

悲观锁:悲观锁思想认为如果多个线程中使用共享资源,则它们肯定会同时进行修改从而引起冲突,悲观锁的解决方式是共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

乐观锁:乐观锁思想认为如果多个线程使用共享资源,它们修改应该是有先后顺序的,不会同时进行修改,如果真的有冲突则后面修改失败。乐观锁的解决方式是共享资源可以由多个线程同时访问修改,对于冲突失败,让其重试直到成功即可。

什么是CAS

CAS全程是compareAndSwap,维基百科上的中文称为“比较并交换”,是乐观锁的一种实现方式,涉及有三个操作数,内存位置(V)、预期值(A)和新值(B),如语句CAS(V,A,B),当*V等于A时,则将值替换为新值B。虽然从语言描述上来说是分为多个操作的,但实际上CAS操作是一个原子操作,是基于CPU提供的原子操作指令实现的。如下CAS实现更新的伪代码:

 do {
        A=current();//*V赋予A,A为就是预期值

        B=update();//获取B作为新值

    } while(!compareAndSwap(V, A, B));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

*V:使用了C语言描述,表示V内存位置的值,便于描述 
更新:我们所说的更新是指更新内存位置V的值,也就是改变*V

上面伪代码将*V赋予A,然后获取新值赋予B,最后进行交换判断直到成功,例如:

- 1 2 3 4 5 6
线程1 A=5 B=6       CAS:false
线程2     A=5 B=7 *V=7  

上面两个线程同时进入循环进行进行更新操作,第一轮循环中只有线程2更新成功,线程1因为*V的值被线程2改变导致和预期值不一致从而失败,只能重新进入下一轮循环直到成功。

CAS解决了什么问题

其实这个问题前面介绍悲观锁和乐观锁时其实已经回答,当多个线程操作时,它解决了悲观锁使用了独占锁,一次只能有一个线程进入临界区的问题,在竞争状态比较低的情况下提高了并发性能。为何说是竞争低的情况下,如果上面有很多个线程同时进入循环,那么每个线程都在占用资源执行,但每次只有一个线程能更新成功。

CAS缺点

1.当竞争很强烈时,每个线程都占用资源执行但是只有一个成功

2.ABA问题,上面的例子中,如果还有一个线程3在修改,并将线程2修改后的值又变成了5,那么线程1此时是察觉不到的,它还能进行成功的执行!例如:

- 1 2 3 4 5 6 7 8 9
线程1 A=5 B=6             *A=6
线程2     A=5 B=7 *V=7        
线程3           A=7 B=5 *A=5  

ABA问题解决方式:引入一个版本号,在每次更新后都加1,在上面的例子中,即时线程3将*V修改回来原值,因为版本号不一致也会导致失败重试。

CAS在java.util.concurrent有着广泛的使用,如AtomicInteger。且对于ABA问题,Java也提供了AtomicStampedReference来处理。

猜你喜欢

转载自blog.csdn.net/qq_39781497/article/details/80174331
今日推荐