十九、深入理解CAS

什么是CAS ?

想进大厂 => 必须深入研究底层 理解计算机网络 操作系统

compareAndSet(int expect , int update) : 比较并交换

expect => 期望值

update => 更新值

如果期望值达到了,就更新为update的值,否则就不更新

package com.liu.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {

    // CAS compareAndSet : 比较并交换

    public static void main(String[] args) {

        AtomicInteger atomicInteger = new AtomicInteger(2020);


        atomicInteger.compareAndSet(2020, 2021);

        System.out.println(atomicInteger.get());

    }
}

CAS 是 CPU的并发原语

原子类 AtomicInteger :声明的变量 :Unsafe 类 + VALUE

Java JUC并发之深入理解CAS原理_java

Java 自身是无法操作内存的;C++可以直接操作内存

因此Java可以通过Unsafe类 调用C++ 底层(native 方法)来间接操作内存

原子类的getAndIncrement() 底层如何实现?

Java JUC并发之深入理解CAS原理_悲观锁_02

自旋锁 : 通过do{} while() 实现 不停旋转(循环)直到得到了某个值才停止

Java JUC并发之深入理解CAS原理_c++_03

内存操作 :实现+1 效率很高

Java JUC并发之深入理解CAS原理_自旋锁_04

最终发现compareAndSet调用的是本地方法 compareAndSetInt()

Java JUC并发之深入理解CAS原理_悲观锁_05

CAS : Unsafe类 继续深入 即是 :ABA问题!

小结 :

  • CAS : 比较当前工作内存中的值和主内存的之,如果这个值是期望的,那么就执行操作,如果不是则一直循环(自旋锁) => 自带原子性 且 不用切换线程状态 提升性能
  • 缺点 :
    1. 循环会耗时
    2. 一次性只能保证一个共享变量的原子性
    3. 会导致ABA问题

什么是ABA问题? 狸猫换太子

ABA 问题的过程是当有两个线程 T1 和 T2 从内存中获取到值A,线程 T2 通过某些操作把内存 值修改为B,然后又经过某些操作将值修改为回值A,T2退出。 线程 T1 进行操作的时候 ,使用预期值同内存中的值比较,此时均为A,修改成功退出。 但是此时的A以及不是原先的A了,这就是 ABA 问题,

package com.liu.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {

    // CAS compareAndSet : 比较并交换

    public static void main(String[] args) {

        AtomicInteger atomicInteger = new AtomicInteger(2020);

        System.out.println(atomicInteger.compareAndSet(2020, 2021));


        System.out.println("=========捣乱线程===============");
        System.out.println(atomicInteger.compareAndSet(2021, 2020));

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        // System.out.println(atomicInteger.get());

        // atomicInteger.getAndIncrement();

        System.out.println("=============预期结果=============");
        System.out.println(atomicInteger.compareAndSet(2021, 6666));
        System.out.println(atomicInteger.get());

    }
}

乐观锁? vs 悲观锁?

对于平时写的SQL: 使用乐观锁(默认其他线程没有修改过数据)

悲观锁:必须判断变量值是否被其他线程修改过!