AtomicStampedReference源码解析

一、前言

    AtomicInteger、AtomicLong等这类Atomic类都不能避免ABA问题,但项目中经常需要避免这类问题,此时可采用AtomicStampedReference进行辅助开发。AtomicStampedReference内部本身不能避免ABA,但它通过设置版本号,每次设置完成后对版本号进行比对,以此验证更新是否正确,不正确则重试。

二、源码解析

    AtomicStampedReference是一个泛型类,并且没有继承任何方法和实现任何接口,内部通过一个内部类Pair对象来存储数据。

private static class Pair<T> {
    //当前对象的引用
    final T reference;
    //当前版本号
    final int stamp;
    private Pair(T reference, int stamp) {
        this.reference = reference;
        this.stamp = stamp;
    }
    //利用reference和stamp生成一个Pair对象,AtomicStampedReference中cas就是基于这个方法的
    static <T> Pair<T> of(T reference, int stamp) {
        return new Pair<T>(reference, stamp);
    }
}

变量说明

//当前对象,包括对象引用和版本号
private volatile Pair<V> pair;
//unsafe常量,设置为使用Unsafe.compareAndSwapInt进行更新,方法中多处需要使用unsafe变量
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
//利用unsafe的本地方法来获取对象在内存的偏移地址
private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);

构造方法

//构造方法利用initialRef和initialStamp生成当前对象,包括对象引用和版本号
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}

AtomicStampedReference中重要的方法

//利用unsafe的CAS方法来对对象进行交换和设置,AtomicStampedReference内部很多方法基于此方法实现的
private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

常用方法

① getReference()方法。

//获取当前对象的引用
public V getReference() {
    return pair.reference;
}

② getStamp()方法。

//获取当前对象的版本号
public int getStamp() {
    return pair.stamp;
}

③ get(int[] stampHolder)方法。

//返回引用和戳记的当前值。
public V get(int[] stampHolder) {
    Pair<V> pair = this.pair;
    //将版本号赋值给数组的第一个元素
    stampHolder[0] = pair.stamp;
    //返回对象的引用
    return pair.reference;
}

    备注:我对参数 int[] stampHolder 的理解就是当参数是全局变量的时候,可以获取其stampHolder[0]的stamp值,不然局部变量的话就当这个方法结束运行这个局部变量就会跟着Java虚拟机栈的这个方法的栈帧清除。

④ compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)方法。

//利用expectedReference和newReference以及expectedStamp和newStamp进行cas
//解决的ABA问题
public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    //获取当前对象
    Pair<V> current = pair;
    return
        //判断当前对象和预期对象的引用是否一样
        expectedReference == current.reference &&
        //判断当前对象和预期对象的版本号是否一样
        expectedStamp == current.stamp &&
        //如果新的对象引用和当前对象引用一样 并且
        //新的对象的版本号和当前对象的版本号一样 则 返回true
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
        //或者通过cas方法将当前对象设置为新的对象,并且更新版本号
         casPair(current, Pair.of(newReference, newStamp)));
}

⑤ set(V newReference, int newStamp)方法。

//对当前的对象更新为新的对象引用以及版本号
public void set(V newReference, int newStamp) {
    Pair<V> current = pair;
    //如果当前对象的引用不等于新的对象的引用 或者 新的版本号不等于当前对象的版本号
    if (newReference != current.reference || newStamp != current.stamp)
        //将当前对象设置为新的对象引用和版本号
        this.pair = Pair.of(newReference, newStamp);
}

⑥ attemptStamp(V expectedReference, int newStamp)方法。

/**
 * 如果当前引用是{@code ==},则自动将戳记的值设置为给定的更新值。
 * 这个操作的任何给定调用都可能会错误地失败(返回{@code false}),
 * 但是当当前值持有期望值并且没有其他线程也试图设置该值时,重复调用将最终成功。
 * 一句话,就是更新当前对象的版本号
 */
public boolean attemptStamp(V expectedReference, int newStamp) {
    Pair<V> current = pair;
    return
        //如果当前的对象引用等于预期对象的引用 并且
        expectedReference == current.reference &&
        //新的对象的版本号等于当前对象的版本号 则返回ture
        (newStamp == current.stamp ||
         //将当前对象设置为新的版本号和对象引用
         casPair(current, Pair.of(expectedReference, newStamp)));
}

三、总结

    个人觉得只要理解compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)方法,那么AtomicStampedReference就可以基本理解了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值