JUC(15)CAS与ABA问题

本文详细介绍了Java中的CAS(Compare and Swap)机制,包括AtomicInteger类的使用示例和CAS的底层实现。CAS是基于Unsafe类的硬件级别原子操作,用于保证并发场景下的原子性。然而,CAS存在循环时间过长、只能保证单个共享变量原子操作以及ABA问题等缺点。文章还通过代码示例展示了ABA问题的发生情况。

1.什么是CAS

Compare and Swap, 翻译成 比较并交换,是java.util.concurrent.atomic包下的类里面的CompareAndSet()方法。它的功能是判断内存某个位置的值是否为预期值,如果是则更新为新的值,这个过程是原子的。

示列:

public class CASDemo {
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
 	   	AtomicInteger atomicInteger = new AtomicInteger(2020);
	    // 期望、更新
		// public final boolean compareAndSet(int expect, int update)
		// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!
		System.out.println(atomicInteger.compareAndSet(2020, 2021));
		System.out.println(atomicInteger.get());
		System.out.println(atomicInteger.compareAndSet(2020, 2021));
		System.out.println(atomicInteger.get());
	}
}

AtomicInteger 是一个原子操作类,而原子操作类是指的是java.util.concurrent.atomic包下,一系列以Atomic开头的包装类。如AtomicBoolean,AtomicUInteger,AtomicLong。它们分别用于Boolean,Integer,Long类型的原子性操作。来保证了原子性。
而Atomic操作类的底层正是用到了“CAS机制”。

2. CAS原理

原子操作类的底层是如何实现CAS的?


  public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
-----------------------------------------------------    
     private static final Unsafe unsafe = Unsafe.getUnsafe();//获取unsafe类的实列对象
   	 private static final long valueOffset;  

  	 static {
        try {
            valueOffset = unsafe.objectFieldOffset //获取某个字段相对Java对象的“起始地址”的偏移量
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

 private volatile int value; //给定的初始值value = 5 相当于 int i = 1;

----------------------------------------------
//调用底层方法实现
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
-----------------------------------------------


compareAndSet方法实际上调用的是unsafe 类的compareAndSwapInt 。compareAndSwapInt是本地方法(使用native关键字),调用c++来调用底层。

1.unsafe类是什么?

Unsafe 是 CAS 的核心类,Java 无法直接访问底层操作系统,而是通过本地 native 方法来访问。不过尽管如此,JVM还是开了一个后门:Unsafe ,它提供了硬件级别的原子操作。UnSafe类在于sun.misc包中, 其内部方法操作可以向C的指针一样直接操作内存,因为Java中CAS操作依赖于UNSafe类的方法.

2.我们不能直接查看native方法是如何使用c++实现的,那CAS的底层的怎么使用Java实现的呢?

原子操作类的自增方式atomicInteger.getAndIncrement();也是通过CAS原理实现的,我们可以通过其去看CAS的实现。


 	public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

---------------------------------------------

  public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        /*
        var代表当前对象,	var2代表内存偏移量。
        方法作用就是当前对象的内存偏移量位置取到的值是否和我们先前取到的var5值相同,
        如果相同值就变成了var5+var4,也就是值加1。方法成功返回true取否为flase,从而退出循环!返回var5以前的值
		如果当前对象这个内存偏移量的值与我们先前取到的var5不相同,则不加1返回false取否变为true,再次循环,直到比较成功而更新值,
		返回以前的值!这就是自旋锁!	
        */
        do {
            var5 = this.getIntVolatile(var1, var2); //获取内存地址的值
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

CAS: 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就
一直循环!

2.CAS的缺点

1.循环时间太长
2.只能保证一个共享变量原子操作
3.ABA 问题

3.ABA问题

什么是aba问题?是由CAS引发的问题,从上面可以知道CAS可以保证其中的值是否为期望值,但是假如有一个线程A执行的时候,执行
compareAndSet(1, 2),这个时候线程B插入进来将期望值1修改为3,然后再修改为1。但是线程a发现期望值没有改变,则会继续执行。

测试:

public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1);

// ============== 捣乱的线程 ==================
System.out.println(atomicInteger.compareAndSet(1, 3));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(3, 1));
System.out.println(atomicInteger.get());
// ============== 期望的线程 ==================
System.out.println(atomicInteger.compareAndSet(1, 2));
System.out.println(atomicInteger.get());
}
------------------------
结果:
true
3
true
1
true
2
 

测试发现CAS不能解决ABA问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值