JUC_CAS

CAS?

CAS(Compare And Swap)意为比较并交换,是基于硬件指令实现的同步原语;Unsafe类提供的本地(native)方法中包含了许多实现CAS操作的本地方法,JUC(java.util.concurrent)中许多同步类就是基于此构建的

Unsafe类?

Java无法直接访问底层操作系统,若想访问则需通过本地方法,Unsafe类中提供了许多本地方法,其中包含了CAS操作的本地方法

    //参数分别为:目标对象 内存地址偏移量 预期值 交换值
    //方法作用为:若对象的内存地址偏移var2处的字段的值与预期值相等 则将该值修改为交换值
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

    //获取静态字段的内存地址偏移量
    public native long staticFieldOffset(Field var1);
    //获取字段的内存地址偏移量
    public native long objectFieldOffset(Field var1);

关于内存地址偏移量与内存地址偏移处的字段的值,以原子类AtomicInteger为例:

    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); }
    }

    private volatile int value;

AtomicInteger的数据储存于value字段,内存地址偏移量储存于valueOffset字段

从静态代码块中不难看出,valueOffset字段储存的内存地址偏移量,即为value字段的内存地址偏移量,显然可知在CAS操作中,value为内存地址偏移处的字段,value的值为被比较并交换的值

CAS存在的问题

资源消耗:线程竞争有时会导致CAS失败,此时CAS失败的线程会进入自旋,直至CAS成功;所以在线程竞争激烈的情况下,CAS会造成大量的资源消耗,影响程序性能

ABA问题:线程A,B对同一变量进行修改,A对于变量的期望值为10;在A对变量进行CAS之前,B将变量的值修改为了11,但又迅速地修改回了10,此时A对变量进行CAS将成功更新变量的值;简而言之,狸猫换太子

问题说明

关于自旋,以AtomicInteger为例:

当前线程通过getIntVolatile方法获取字段的值,储存于var5;但是在执行compareAndSwapInt方法之前,字段的值被修改,导致CAS失败;此时线程将进入自旋,直到CAS成功为止

//自旋锁
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;
}

关于ABA问题,示例代码:

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

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("CAS是否成功: " + atomicInteger.compareAndSet(1, 2));
        }).start();

        new Thread(()->{
            atomicInteger.set(2);
            System.out.println("atomicInteger被修改为: " + atomicInteger.get());
            atomicInteger.set(1);
            System.out.println("atomicInteger被修改回: " + atomicInteger.get());
        }).start();
    }
}

原子引用解决ABA问题

原子引用分为AtomicReference与AtomicStampedReference,区别在于是否带有版本号,前者无法解决ABA问题,但后者可以;换言之,带版本号的原子引用,实际上是乐观锁的一种实现

示例代码:

public class AtomicReferenceDemo {
    public static void main(String[] args) {
        //AtomicStampedReference<> 带版本号
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(5, 1);

        new Thread(() -> {
            int stamped = atomicStampedReference.getStamp();
            Integer reference = atomicStampedReference.getReference();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("CAS是否成功: " + atomicStampedReference.compareAndSet(reference, reference + 1, stamped, stamped + 1));
        }).start();

        new Thread(() -> {
            new Thread(() -> {
                int stamped = atomicStampedReference.getStamp();
                Integer reference = atomicStampedReference.getReference();
                atomicStampedReference.compareAndSet(reference, reference + 1, stamped, stamped + 1);
                System.out.println("atomicStampedReference被修改为: " + atomicStampedReference.getReference());

                int stamped2 = atomicStampedReference.getStamp();
                Integer reference2 = atomicStampedReference.getReference();
                atomicStampedReference.compareAndSet(reference2, reference2 - 1, stamped2, stamped2 + 1);
                System.out.println("atomicStampedReference被修改回: " + atomicStampedReference.getReference());
            }).start();
        }).start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值