浅析CompareAndSet(CAS)底层

浅析CompareAndSet(CAS)底层源码

java.util.concurrent.atomic 这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由 JVM从等待队列中选择一个另一个线程进入。

包结构图

在这里插入图片描述
CAS就是Compare and Swap,比较并交换算法,处理同步问题的常见解决思路。需要有4个值:当前对象(this),内存地址(valueOffset),旧的预期值(expect),即将要更新的目标值(update)。CAS指令执行时,当且仅当内存地的值与预期值相等时,将内存地址的值修改为目标值,并返回true ,否则就什么都不做,返回false。整个比较并替换的操作是一个原子操作。

测试用例:
public class CASTest {
    public static void main(String[] args) {
         //设定初始值为10
        AtomicInteger integer = new AtomicInteger(10);

        /**
         * 一个是期望值,一个是更新值,但期望值和原来的值相同时,才能够更改
         */
        //设定期望值10,更新值15,并获取当前内存里的值
        boolean flag=integer.compareAndSet(10, 15);
        System.out.println(flag + "\t 当前值: " + integer.get());
        //参照组1:设定期望值10,更新值20,并获取当前内存里的值
        flag=integer.compareAndSet(10, 20);
        System.out.println(flag+ "\t 当前值: " + integer.get());
        //参照组2:设定期望值15,更新值20,并获取当前内存里的值
        flag=integer.compareAndSet(15, 20);
        System.out.println( flag+ "\t 当前值: " + integer.get());
    }
}

----返回结果------
true	 当前值: 15
false	 当前值: 15
true	 当前值: 20
compareAndSet方法的底层源码:
 /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

在多线程情况下容易造成"ABA问题"的产生,CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化

ABA问题测试用例
public class ABADemo {

    public static void main(String[] args) {

        //设定初始值为10
        AtomicInteger integer = new AtomicInteger(10);

 new Thread(()->{
            boolean flag=integer.compareAndSet(10, 15);
            System.out.println(Thread.currentThread().getName()+"\t "+flag + "\t 当前值: " + integer.get());
            flag=integer.compareAndSet(15, 20);
            System.out.println(Thread.currentThread().getName()+"\t "+ flag+ "\t 当前值: " + integer.get());
        },"thread-1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean flag=integer.compareAndSet(20,10);
            System.out.println(Thread.currentThread().getName()+"\t "+flag+ "\t 当前值:  "+integer.get());

        },"thread-2").start();

    }
}

----返回结果------
thread-1	 true	 当前值: 15
thread-1	 true	 当前值: 20
thread-2	 true	 当前值:  10

解决ABA问题可以使用添加版本号来解决变更数据问题

解决ABA问题测试用例
public class ABADeal {
    public static void main(String[] args) {

         AtomicStampedReference<Integer> atomic = new AtomicStampedReference(10, 0);

        new Thread(() -> {
            int stamp = atomic.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t "+null+ "\t "+ " 第1次版本号: " + stamp+"\t 当前值:  "+atomic.getReference());

            boolean flag=atomic.compareAndSet(10, 15,
                    atomic.getStamp(), atomic.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() +"\t "+flag+"\t "+ " 第2次版本号: " + atomic.getStamp()+"\t 当前值:  "+atomic.getReference());

            flag=atomic.compareAndSet(15, 20,
                    atomic.getStamp(), atomic.getStamp() + 1);
            System.out.println(Thread.currentThread().getName()  +"\t "+flag+"\t "+ " 第3次版本号: " + atomic.getStamp()+"\t 当前值:  "+atomic.getReference());

        }, "thread-1").start();


        new Thread(() -> {
            int stamp = atomic.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t "+null+ "\t "+ " 第1次版本号: " + stamp+"\t 当前值:  "+atomic.getReference());

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean flag = atomic.compareAndSet(20, 10,
                    stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "\t "+ flag+"\t "+" 第2次版本号: " + atomic.getStamp()+"\t 当前值:  "+atomic.getReference());

        }, "thread-2").start();

    }
}

----返回结果------
thread-1	 null	  第1次版本号: 0	 当前值:  10
thread-2	 null	  第1次版本号: 0	 当前值:  10
thread-1	 true2次版本号: 1	 当前值:  15
thread-1	 true3次版本号: 2	 当前值:  20
thread-2	 false2次版本号: 2	 当前值:  20

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值