【并发编程笔记】 ---- CAS深入分析

文章目录

1. 什么是CAS
2. AtomicInteger
3. CAS缺陷
4. ABA问题及解决方案

1. 什么是CAS

CAS是Compare and swap的缩写,即比较交换。由JDK提供的非阻塞原子性操作,它通过硬件保证了比较更新操作的原子性。

CAS算法过程: 它包含3个参数 CAS(V,A,B)

  • V: 表示要更新的变量(内存值)
  • A: 表示预期值(旧的)
  • B: 表示要更新的值(新值)

当且仅当内存值V的值等于旧的预期值A时,才会将内存值V的值修改为B,否则什么都不干
在这里插入图片描述

J.U.C下的Atomic类,都是通过CAS来实现的。以AtomicInteger为例,来阐述CAS的实现,如下:

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
  • Unsafe是CAS的核心类,Java无法直接访问底层操作系统,而是通过本地native方法来访问。不过尽管如此,JVM还是开了一个后门:Unsafe,它提供了硬件级别的原子操作
  • valueOffset 为变量值在内存中的偏移地址,Unsafe就是通过偏移地址来得到数据的原值的
  • value当前值,使用 volatile修饰,保证多线程环境下看见的是同一个

2.AtomicInteger

以AtomicInteger的 # addAndGet()方法来说明,源代码:

public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
}
  • 内部调用unsafe#getAndAddInt(Object var1, long var2, int var4)方法,在#getAndAddInt(Object var1, long var2, int var4)方法中,主要看compareAndSwapInt(var1, var2, var5, var5 + var4)方法,代码如下:
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    
    该方法为本地方法,有四个参数,操作含义: 如果对象obj中内存偏移量var2的变量值为var4,则使用新的值var5替换旧的值var4
  • var1: 对象内存位置
  • var2: 对象中的变量的偏移量
  • var4: 变量预期值(旧的)
  • var5: 新的值

最终底层实现(仅了解)
在这里插入图片描述
最终实现: cmpxchg = cas修改变量值
lock cmpxchg 指令
硬件: lock指令在执行后面指令的时候锁定一个北桥信号(不采用锁总线的方式)

3. CAS缺陷

CAS虽然高效解决了原子操作,但是还是存在一些缺陷,主要体现在三个方面:

  • 循环时间太长

    CAS里面是一个循环判断的过程, 如果自旋 CAS 长时间地不成功,CPU资源会被一直占用。
    在 J.U.C 中,有些地方就限制了 CAS自旋的次数,例如: BlockingQueue 的 SynchronousQueue 。

  • 只能保证一个共享变量原子操作

    CAS 的实现只能针对一个共享变量,如果是多个共享变量就只能使用锁了。
    如果有办法把多个变量整成一个变量,利用 CAS也不错。例如读写锁中 state 的高低位。

  • ABA问题

    CAS 需要检查操作值有没有发生改变,如果没有发生改变则更新。
    一个线程 a 将数值改成了 b,接着又改成了 a,此时 CAS 认为是没有变化,其实是已经变化过了,解决方案可以使用版本号标识,每操作一次version 加 1。在 java5 中,已经提供了 AtomicStampedReference 来解决问题。

4. ABA问题及解决方案

4.2 原因

CAS 需要检查操作值有没有发生改变,如果没有发生改变则更新。
但是存在这样一种情况:如果一个值原来是 A,变成了 B,然后又变成了 A,那么在 CAS 检查的时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。

4.1 解决方案

对于 ABA 问题其解决方案是加上版本号,即在每个变量都加上一个版本号,每次改变时加 1 ,即 A —> B —> A ,变成1A —> 2B —> 3A 。

Java 提供了 AtomicStampedReference 来解决。AtomicStampedReference 通过对对象标记版本戳 stamp ,从而避免 ABA 问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值