聊聊 CAS

Posted Mr.袋鼠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊 CAS相关的知识,希望对你有一定的参考价值。

哥有故事,你有酒,长夜漫漫,听我给你说。

 参考资源:

https://blog.csdn.net/hsuxu/article/details/9467651

 

1.概述

CAS,compare and swap ,“比较交换”的意思。它是一种并发状态下的,比较交换的策略。

想必,我们一定听说过乐观锁的概念,并发中乐观锁的核心概念就是应用了CAS。它包含了三个值:内存值,预期值和更新值。当内存值和预期值相等时,就会使用更新值将原来的数据(预期值)进行更新;如果不相等,则什么都不做。

 

2.例子

举个经典的例子,帮助大家理解。(伪代码)

 1  public class AtomicInt {
 2    private volatile int value;
 3    public final int get() {
 4        return value;
 5     }
 6 
 7 publicfinal int getAndIncrement() {
// 下面的for的无限循环,就是经典的CAS自旋(Compare and swap)
8 for (;;) { 9 int current = get(); 10 int next = current + 1; 11 if (compareAndSet(current, next)) 12 return current; 13 } 14 } 15 16 public final boolean compareAndSet(int expect, int update) { 17 unsafe.compareAndSwapInt方法(方法内部,使用JNI调用C的代码); 18 } 19 }

 

3.凡事问个为什么.

为什么要比较?为什么要用CAS的自旋?直接设值不行吗?它有什么优点缺点?

例如,i++ 这个简单的操作不是一步完成的,而是分了三步。第一步,取值,第二步加一,第三部更新值。

假设有A,B两个线程同时操作i++这个处理,那么,当线程A完成上述第二步的时候,线程B已经将I的值更新(第三步)做完了。这样就会导致值发生异常,就是所谓的线程不安全。

所以,利用了乐观锁的思想采用了自旋的方式,每一次,就会先取得加一后的值,再将旧的值和内存中的值进行比较,如果相等,则说明,没有被别的线程动过,因此可以正常更新;如果不相等,则说明值已经被更新了,放弃本次的操作,从头再来,再重新取值,加一,更新。

 

CAS自旋(乐观锁)避免了悲观锁独占的现象,同时提高了并发的性能。但是,它也是有缺点的。(第三点参考了其他文章)

①乐观锁只能保证一个变量的的原子操作,多个变量的话,就没有办法了。

②长时间自旋,导致CPU消耗过大。

③ABA问题。CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但是,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上,被其他线程改过。这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。

 

就聊这么多,祝君好梦!

 

以上是关于聊聊 CAS的主要内容,如果未能解决你的问题,请参考以下文章

Java——聊聊JUC中的CAS原理

Java——聊聊JUC中的CAS原理

Java——聊聊JUC中的CAS原理

聊聊并发——CAS算法

聊聊公平锁和非公平锁

CAS登录后回传除了ticket参数以外的其他自定义参数