Java并发编程-AtomicInteger-CAS底层原理

AtomicInteger

AtomicInteger是使用CAS来保持原子性。而Unsafe是CAS的核心类,由于Java方法无法直接访问操作系统低层,需要通过本地native方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据

Unsafe

  // 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); }
    }
	//用volatile修饰value值,保持可见性
    private volatile int value;
    

Unsafe类中的方法大多数都被native修饰,也就是说Unsafe类直接调用操作系统的底层资源去执行相应任务。

  /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
    	//变量,内存偏移量,加1
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

CAS(Compare-And-Swap)一条cpu并发原语

判断内存中某个位置的值是否为预期值,如果是则更改为新值,这个过程是原子的。

JVM会帮我们实现出CAS汇编指令,完全依赖于硬件的功能,通过它实现了原子操作。
CAS是一种系统原语,由若干条指令组成。且原语的执行必须是连续的,执行过程中不允许被打断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题

//对象,内存偏移量,增加量
public final int getAndAddInt(Object var1, long var2, int var4) {
		//var5存储的是期望值
        int var5;
        //自旋锁基本思想
        do {
        	//获得主物理内存中的值
            var5 = this.getIntVolatile(var1, var2);
        }//比较并交换 
        while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
		//返回修改后的值
        return var5;
    }

CAS缺点

  1. 在多线程下,可能有的线程倒霉一直有别的线程去修改值,导致长时间循环,CPU浪费
  2. 只能解决一个变量的线程安全问题
  3. ABA问题

ABA问题

假设有三个线程,线程A、B、C,同时去修改变量d=1,A想把d修改为2,在此之前B把d修改为了3,C把d又修改成了1,则A去修改d的时候没有发现变量d被修改过,直接修改。

CAS不在乎中间发生了什么,只在乎在作比较的时候值是否相等。中间过程容易出现问题

解决ABA问题

原子引用AtomicReference


class User {

    String userName;
    int age;

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

public class AtomicReferenceDemo {

    public static void main(String[] args) {
        User z3 = new User("z3", 114);
        User l4 = new User("l4", 116);
        AtomicReference<User> userAtomicReference = new AtomicReference<>();
        userAtomicReference.set(z3);
        //第一次修改引用,成功
        System.out.println(userAtomicReference.compareAndSet(z3, l4)+"\t "+userAtomicReference.get());
        //第二次修改引用,失败
        System.out.println(userAtomicReference.compareAndSet(z3, l4)+"\t "+userAtomicReference.get());

    }
}

ABA问题,在做每次修改的时候,在数据后加上一个版本号,每修改一次,版本号加一。

AtomicStampedReference

利用版本号(时间戳)来保证解决ABA的问题,也就是说保证每一行的数据都是保证唯一的。

		new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + atomicStampedReference.getStamp());

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

            atomicStampedReference.compareAndSet(100, 101, 1, 2);
            System.out.println(Thread.currentThread().getName() + "\t第二次版本号:" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, 2, 3);
            System.out.println(Thread.currentThread().getName() + "\t第三次版本号:" + atomicStampedReference.getStamp());
        }, "t3").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + atomicStampedReference.getStamp());
            try {
                //暂停t4 3s
                //保证t1完成一次ABA
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(100, 400, 1, 2) +
                    "\t当前版本号:" + atomicStampedReference.getStamp() + "\t当前最新值:" + atomicStampedReference.getReference());
        }, "t4").start();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值