JUC之atomic

场景

比如一个int类型的变量,多线程下去操作这个变量,这时候吧比加锁更好的解决方法就是atomicInteger

另外AtomicReference主要是用在了自旋锁

automic原理-CAS

CAS是指Compare And Swap比较并交换,是一种很重要的同步思想。如果主内存的值跟期望值一样,那么就进行修改,否则一直重试,直到一致为止。

我们在看原子性的时候用了AtomicInteger来实现了和加锁sync一样的效果

查看AtomicInteger.getAndIncrement()方法,发现源码里根本没有用到synchronized也实现了同步。这是为什么?
JMM

CAS底层原理

  1. volatile int value保证线程间的可见性
  2. 利用UnSafe类去操作底层操作系统,UnSafe类都是native方法
  3. valueOffset来记录变量在内存的偏移地址
//源码片段
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
...
private volatile int value;
...
public final int getAndIncrement(){
    return unsafe.getAndAddInt(this,valueOffset,1);
}

public final int getAnddAddInt(Object var1,long var2,int var4){
    int var5;
    //在这用了一个循环,不停的去比较
    do{
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}
//源码片段
public final class Unsafe {
    ...
    public native void putByte(Object var1, long var2, byte var4);
    public native short getShort(Object var1, long var2);
    ...
}

比如主内存的变量V,T1线程本地内存拿到的时候是A,把他修改为B
如果中间线程T2修改了V,由于加了volatile,所以这个修改是立即可见的
A线程发现这个时候主内存的值不等于快照值
所以继续循环,重新从主内存获取

CAS缺点

CAS实际上是一种自旋锁,

  1. 一直循环,开销比较大,上面源码里可以看到是while循环
  2. 只能保证一个变量的原子操作,多个变量依然要加锁。
  3. 引出了ABA问题。
ABA问题

首先我们可以看到CAS原理只注重头和尾,只要首尾一致就接受。但是有的需求,还看重过程,中间不能发生任何修改,这就引出了AtomicReference原子引用

public class ABADemo {
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        System.out.println("======ABA问题的产生======");

        new Thread(() -> {
            //atomicReference.compareAndSet(100, 101) atomicReference是100就返回true并改为101,不是就返回false什么都不做
            //我先改成101,再改成100
            System.out.println(atomicReference.compareAndSet(100, 101) + "  " + atomicReference.get().toString());
            System.out.println(atomicReference.compareAndSet(101, 100) + "  " + atomicReference.get().toString());
        }, "t1").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //我们这里其实只看他是不是100,你中间做了什么我不管,虽然atomicReference中途被改成了101
            System.out.println(atomicReference.compareAndSet(100, 2019) + "  " + atomicReference.get().toString());
        }, "t2").start();

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

    }
}
======ABA问题的产生======
true  101
true  100
true  2019
AtomicStampedReference解决ABA

使用AtomicStampedReference类可以解决ABA问题。这个类维护了一个“版本号”Stamp,在进行CAS操作的时候,不仅要比较当前值,还要比较版本号。只有两者都相等,才执行更新操作

public class ABADemo {
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        System.out.println("======ABA问题的解决======");
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "  stamp: " + stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100, 101,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "  stamp: " + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "  stamp: " + atomicStampedReference.getStamp());
        }, "t3").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "  stamp: " + stamp);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2019,
                    stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + " " + result + "  stamp" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "  value:" + atomicStampedReference.getReference());
        }, "t4").start();
    }
}
======ABA问题的解决======
t3  stamp: 1
t4  stamp: 1
t3  stamp: 2
t3  stamp: 3
t4 false  stamp3
t4  value:100
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orange大数据技术探索者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值