AtomicStampedReference

AtomicStampedReference阅读笔记

问题

1、ABA是什么?

  • CAS情况下会导致这个发生。

    ABA问题发生在多线程环境中,当某线程连续读取同一块内存地址两次,两次得到的值一样,它简单地认为“此内存地址的值并没有被修改过”,然而,同时可能存在另一个线程在这两次读取之间把这个内存地址的值从A修改成了B又修改回了A,这时还简单地认为“没有修改过”显然是错误的。

2、ABA的危害?

  • 这篇文章的ABA危害写的很好
  • 上面主要用一个栈来模拟,一个first线程cas操作时堵塞了,另外一个second线程去把栈进行B操作 ,再把原值继续改回A,结果first线程cas判定成功

3、ABA的解决方案?

4、AtomicStampedReference是怎么解决ABA的?

  • 使用版本号解决(自定义内部类Pair)

一、简介

优点:

缺点:

二、继承关系图

三、存储结构

自定义Pair内部类数据结构,存储了元素值和版本号

四、源码分析

内部类
private static class Pair<T> {
    final T reference;
    final int stamp;
    private Pair(T reference, int stamp) {
        this.reference = reference;
        this.stamp = stamp;
    }
    static <T> Pair<T> of(T reference, int stamp) {
        return new Pair<T>(reference, stamp);
    }
}
//将元素值和版本号绑定在一起,存储到reference 和stamp中
属性
private volatile Pair<V> pair;
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
//获取pair变量在对象中的内存偏移量
private static final long pairOffset =
        objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
构造
//传入初始值和初始版本号
public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
}
主要方法:compareAndSet()方法
public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair<V> current = pair;
    return
        //预期的值和当前值是否一样 &&
        expectedReference == current.reference &&
        //预期的版本号和当前的版本号一样 &&
        expectedStamp == current.stamp &&
        
        (
        //新值和当前值一样且新的版本和当前值一样,则不做变化,返回即可
        (newReference == current.reference &&
          newStamp == current.stamp) ||
        //cas替换,Unsafe.compareAndSwapObject,比较和替换对象
         casPair(current, Pair.of(newReference, newStamp))
    );
}
使用方式
public static void main(String[] args) throws InterruptedException {
    AtomicStampedReference<String> atomic = new AtomicStampedReference<>("aaa",1);
    //获取元素值(pair.reference)
    System.out.println(atomic.getReference());//"aaa"
    //获取版本号(pair.stamp)
    System.out.println(atomic.getStamp());//1
    //不修改元素值,只修改版本,
    boolean isOk = atomic.attemptStamp("aaa",atomic.getStamp() + 1);
    System.out.println(isOk);//true
    System.out.println(atomic.getStamp());//2
    //修改元素值和版本号
    boolean isOk1 = atomic.compareAndSet(atomic.getReference(),"bbb",atomic.getStamp(),atomic.getStamp() + 1);
    System.out.println(isOk1);//true
    System.out.println(atomic.getReference());//"bbb"
    System.out.println(atomic.getStamp());//3
    //与compareAndSet一样,因为内部直接调用的compareAndSet方法
    boolean isOk2 = atomic.weakCompareAndSet(atomic.getReference(),"ccc",atomic.getStamp(),atomic.getStamp() + 1);
    System.out.println(isOk2);//true
    System.out.println(atomic.getReference());//"ccc"
    System.out.println(atomic.getStamp());//4
    //获得版本号和元素值
    int [] stamp = new int[1];
    String reFenence = atomic.get(stamp);
    System.out.println("stamp:"+stamp[0]);//stamp:4
    System.out.println("reFenence:"+reFenence);//reFenence:ccc
}

五、总结

1、在多线程下使用无锁结构需要注意ABA问题

2、ABA的解决一般使用版本号来控制,并且保证数据结构使用元素值来传递,且每次添加元素都新建节点承载元素值。

3、AtomicStampedReference采用内部Pair类来存储元素值和版本号。

额外:

AtomicMarkableReference 也可以解决ABA问题,他不维护版本号,使用的是一个boolean类型的标记

六、参考

彤的公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值