AtomicStampedReference源码详解

14 篇文章 0 订阅
14 篇文章 0 订阅

AtomicStampedReference源码详解

通过前面几个原子类源码的学习,可以发现这些原子类型实现原子类型的操作,主要是利用volatile和CAS来实现的。其中,volatile关键字可以保证线程可见性,而CAS算法,主要是通过unsafe,利用CPU的指令来实现操作的原子性,CAS算法实现了一种快速失败的方式,当某个线程修改已经被改变的数据时,会快速失败。另外,当CAS算法修改某个数据失败时,由于有自旋算法的加持,对于数据的修改最终会成功。在大多数情况下,通过CAS算法来实现操作的原子性,是没有问题的。唯一例外的情况,就是ABA的问题。

如果,某个数据的值,从A变成B,然后从B由变成A。此时,CAS算法在进行检查的时候,发现数据没有变化,然后就执行更新操作。

但是,实际上,数据是发生了变化的。针对,这种情况,一般是给数据增加一个版本号的属性,当数据发生改变的时候,对版本号也做相应的更新。这样,就可以解决ABA的问题,这就是AtomicStampedReference的使用场景。

类定义

public class AtomicStampedReference<V>{
    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);
        }
    }
}

AtomicStampedReference:在内部定义了一个内部类Pair,主要用来存放引用值和版本号stamp.

属性定义

private volatile Pair<T> pair;
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset = objectField(UNSAFE, "pair", AtomicStampedReference.class);

构造函数

public AtomicStampedReference(V initialRef, int initialStamp){
    pair = Pair.of(initialRef, initialStamp);
}

工具方法

private boolean casPair(Pair<V> cmp, Pair<V> val){
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

static long objectField(sun.misc.Unsafe UNSAFE, String field, Class<?> klazz){
    try{
        return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
    }catch(NoSuchFieldException e){
        NoSuchFieldError error = new NoSuchFieldError(field);
        error.initCause(e);
        throw error;
    }
}

get和set方法

public V getReference(){
    return pair.reference;
}

public int getStamp(){
    return pair.stamp;
}

public V get(int[] stampHolder){
    Pair<V> pair = this.pair;
    stampHolder[0] = pair.stamp;
    return pair.reference;
}

public void set(V newReference, int newStamp){
    Pair<V> current = pair;
    if(newReference != current.reference || newStamp != current.stamp){
        this.pair = Pair.of(newReference, newStamp);
    }
}

这里,需要重点说下方法get(int[] stampHolder)方法,这个方法的意图是获取当前值以及stamp值,但是Java不支持多值的返回,并且在AtomicStampedReference内Pair被定义为私有的,因此在此处就采用了传参的方式来解决这个问题。

比较设置方法

public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp){
    Pair<V> current = pair;
    return expectedReference == current.reference &&
        expectStamp == current.stamp &&
        ((newReference == current.reference &&
         newStamp == current.stamp) || 
         casPair(current, Pair.of(newReference, newStamp)));
}

public boolean weakCompareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp){
    return compareAndSet(expectedReference, newReference, expectedStamp, newStamp);
}

compareAndSet方法,主要是比较并且设置当前的引用值,这和其他原子类的CAS算法相同。这里,唯一区别的地方,是需要去比较版本号,即stamp的值。当expectedReference与当前的引用值相等,并且,expectedStamp与当前的stamp相等时,执行更新操作,把当前的引用值设置为newReference,当前的版本号设置为newStamp,然后返回true,否则,返回false.

更新版本号方法

public boolean attempStamp(V expectedReference, int newStamp){
    Pair<V> current = pair;
    return expectedReference == current.reference &&
        (newStamp == current.stamp || casPair(current, Pair.of(expectedReference, newStamp)));
}

该方法,主要是原子性地更新版本号的值,首先会比较当前引用的对象是不是expectedReference,如果是,则更新版本号的值,并且返回true,否则,返回false.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值