多线程 CAS 机制解析及应用( 原子类 . 自旋锁 )解决 ABA 问题

Posted Perceus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程 CAS 机制解析及应用( 原子类 . 自旋锁 )解决 ABA 问题相关的知识,希望对你有一定的参考价值。

@TOC


CAS ( Compare and swap)

1、解析CAS

CAS:全称 Compare and swap,字面意思:”比较并交换“,一个 CAS 涉及到以下操作:

CAS 伪代码:

下面写的代码不是原子的, 真实的 CAS 是一个原子的硬件指令完成的. 这个伪代码只是辅助理解
CAS 的工作流程.

boolean CAS(address, expectValue, swapValue) 
    if (&address == expectedValue) 
        &address = swapValue;
        return true;
    
    return false;

CAS最大的意义,就是让我们写这种多线程安全的代码,提供了一个新的思路和方向 (就和锁不一样了)

很多功能,既可以是硬件实现,也可以是软件实现

就像刚才这段比较交换逻辑,这就相当于硬件直接实现出来了,通过这一条指令,封装好,直接让咱们用了


2、CAS 应用

1. 基于CAS 能够实现“原子类”

Java 标准库中提供了一组原子类,针对所常用多一些 int, long, int array… 进行了封装,可以基于 CAS 的方式进行修改,并且线程安

java.util.concurrent.atomic 包,里面的类都是基于这种方式来实现的

import java.util.concurrent.atomic.AtomicInteger;

public class Demo27 
    public static void main(String[] args) throws InterruptedException 
        AtomicInteger num = new AtomicInteger(0);

        Thread t1 = new Thread(() -> 
            for (int i = 0; i < 50000; i++) 
                // 这个方法就相当于 num++
                num.getAndIncrement();
            
        );
        Thread t2 = new Thread(() -> 
            for (int i = 0; i < 50000; i++) 
                // 这个方法就相当于 num++
                num.getAndIncrement();
            
        );

//        // ++num
//        num.incrementAndGet();
//        // --num
//        num.decrementAndGet();
//        // num--
//        num.getAndDecrement();
//        // += 10
//        num.getAndAdd(10);

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        // 通过 get 方法得到 原子类 内部的数值.
        System.out.println(num.get());
    

运行结果:

100000

方法:

num.incrementAndGet(); // ++num
num.decrementAndGet(); // --num
num.getAndIncrement(); // num++;
num.getAndDecrement(); // num--;
num.getAndAdd(10); // += 10

原子类背后具体是怎么实现的

原子类背后具体是怎么实现的?

伪代码实现:

class AtomicInteger 
    private int value;

    public int getAndIncrement() 
        int oldValue = value;
        while ( CAS(value, oldValue, oldValue+1) != true) 
            oldValue = value;
        
        return oldValue;
    

图解:

图上的意思就是:假设两个线程同时调用 getAndIncrement


2. 基于CAS能够实现自旋锁

基于 CAS 实现更灵活的锁,获取到更多的控制权

自旋锁伪代码 :

public class SpinLock 
    private Thread owner = null; // 记录下当前锁被哪个线程持有了,为 null 表示当前未加锁

    public void lock()
    // 通过 CAS 看当前锁是否被某个线程持有.
    // 如果这个锁已经被别的线程持有, 那么就自旋等待.
    // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.
        while(!CAS(this.owner, null, Thread.currentThread()))
        
    

    public void unlock ()
        this.owner = null;
    

图解:


CAS中的ABA问题

ABA问题:就是CAS中的关键【先比较,在交换】

而这里的比较,其实是在比较 当前值 和 旧值 是不是相同的。

把这两个值相同的情况,就视为中间没有发生过改变。

所谓的ABA 指的是:


举—个典型的例子,ABA问题产生的bug:

int oldValue = value; //读取旧值
CAS(&value, oldValue, oldValue - 50)

当按下取款的操作的时候,机器卡了一下,滑稽多按了—下取款~

这就相当于,一次取钱操作,执行了两遍,(两个线程,并发的去执行这个取钱操作),咱们的预期效果应该是只能取成功一次!(希望取走50,账户还剩50)

假设在取款的一瞬间,滑稽的朋友给他转了50此时就会触发ABA问题

卡了和转了50,这两次巧合导致了一个存在 BUG 的 ABA 问题 (极端场景的问题)

哪怕这样的 bug 出现概率是 0.01%,咱们也需要处理!!!


如何解决ABA问题


拓展:


相关面试题

① 讲解下你自己理解的 CAS 机制


② ABA问题怎么解决?


以上是关于多线程 CAS 机制解析及应用( 原子类 . 自旋锁 )解决 ABA 问题的主要内容,如果未能解决你的问题,请参考以下文章

多线程 -- CAS自旋锁Atomic类

ava多线程系列 JUC原子类 CAS及原子类

多线程多线程面试常见基础内容

多线程多线程面试常见基础内容

多线程多线程面试常见基础内容

[多线程进阶]CAS与Synchronized基本原理