CAS与Unsafe讲解

11 篇文章 0 订阅

1. 什么是CAS

CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的并发原语,其作用是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。

CAS并发原语体现在java语言中就是sun.misc.Unsafe类中的各种方法。调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过过程中不予许被中断,也就是说CAS是一条CPU的原语指令,不会造成所谓的数据不一致问题。


2.Unsafe类

Unsafe是CAS的核心类,由于java方法无法直接访问底层系统,需要用过本地方法(native)来访问,Unsafe相当于一个后门,基于类可以直接操作特定内存的数据。Unsafe类位于sun.misc包中,其内部方法操作可以像C的指针一样操作内存,因为CAS操作是执行依赖于Unsafe类的方法。


3.AtomicInteger源码

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // rt.jar中的sun.misc.Unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    
	// 内存偏移量,变量在内存中的地址
    private static final long valueOffset;
	
	// 操作变量加volatile关键字,使得所有的线程都可以访问到。
	private volatile int value;

    static {
        try {
         	// 获取到变量在内存中的地址
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public final int getAndIncrement() {
    	// this为当前操作的对象,valueOffset 为变量的地址, 1 +1
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

}


// rt.jar中sun.misc的Unsafe类
public final class Unsafe {
	// 源码请看下图
	public final int getAndAddInt(java.lang.Object o, long l, int i) { /* compiled code */ }
}

在这里插入图片描述

4.CAS的缺点

  1. 循环时间长,开销大。如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
  2. 只能保证一个共享变量的原子操作。
  3. 会导致ABA问题

5.ABA问题

CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说一个线程t1从内存位置v中取出A,这个事件另一个线程t2为取出A,并且线程t2进行了一些操作将值改成了B,然后线程t2又将B改成了A,这个时候t1线程进行CAS操作的时候发现内存中的值还是A,然后线程t1操作成功。

public class TestAtomicDemo {

    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {

        new Thread(() -> {
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);

            System.out.println("t1做ABA数据交换");
        }, "t1").start();


        new Thread(() -> {

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean flag = atomicReference.compareAndSet(100, 1000);
            System.out.println("t2\t" + flag + "\t" + atomicReference.get());
        }, "t1").start();



        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        // ABA 解决方案:加上版本号的校验,类似于数据库的乐观锁
        new Thread(() -> {

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            int stamp = atomicStampedReference.getStamp();
            System.out.println("t3版本号:" + stamp);

            atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1);
            atomicStampedReference.compareAndSet(101, 100, stamp + 1, stamp + 2);

            System.out.println("t3做ABA数据交换, 版本号为:" + atomicStampedReference.getStamp());
        }, "t3").start();


        new Thread(() -> {

            int stamp = atomicStampedReference.getStamp();
            System.out.println("t4版本号:" + stamp);

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean flag = atomicStampedReference.compareAndSet(100, 1000, stamp, stamp + 1);
            System.out.println("t4\t" + flag + "\t" + atomicStampedReference.getReference());
        }, "t4").start();
    }
}

结果如下,用AtomicStampedReference这个类,加上版本号,可以避免ABA问题
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unsafe类是Java中一个非常特殊的类,它提供了对底层操作的支持,包括内存操作、线程调度等。使用Unsafe类可以实现一些Java中无法实现的操作,并且可以提高代码的性能。 CAS(Compare and Swap)操作是Unsafe类中非常常见的操作之一。它的作用是比较当前值和期望值是否相等,如果相等,则将新值更新到当前值中,否则不做任何操作。CAS操作通常被用于多线程编程中的数据同步,以保证多个线程对共享变量的操作是正确的。 下面是使用Unsafe类进行CAS操作的一个示例: ``` import sun.misc.Unsafe; import java.lang.reflect.Field; public class CASDemo { private static Unsafe unsafe; static { try { // 使用反射获取Unsafe类 Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); unsafe = (Unsafe) field.get(null); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { // 假设初始值为10 int expectedValue = 10; // 将初始值存储在内存地址100的位置 long offset = unsafe.objectFieldOffset(CASDemo.class.getDeclaredFields()[0]); // 使用CAS操作将值从10更新为20 boolean casResult = unsafe.compareAndSwapInt(new CASDemo(), offset, expectedValue, 20); System.out.println(casResult); } private int value = 10; } ``` 在上面的代码中,首先通过反射获取Unsafe类的实例,然后将初始值存储在类的某个字段中,接着使用`unsafe.compareAndSwapInt()`方法进行CAS操作,将值从10更新为20。这个方法的第一个参数是对象实例,第二个参数是字段的内存地址偏移量,第三个参数是期望值,第四个参数是更新值。如果更新成功,该方法返回true,否则返回false。 需要注意的是,Unsafe类使用需要非常小心,因为它可以直接操作内存,容易引发安全问题。在使用Unsafe类的时候,应该先了解清楚相关的API文档,并且在实际应用中要进行严格的测试和验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值