AtomicStampedReference源码详解

一、AtomicStampedReference原子类介绍

1、问题的提出

使用CAS方式会保证对原子类操作是线程安全的,它相较于传统的加锁来操作有一些优势,比如更加轻量级,速度更快;但是它同样也存在一些缺点,比如不能象同步代码块一样保证多个操作是线程安全的、当并发高时多个线程同时执行CAS不成功造成CPU利用率飙升,除了这些它还有一个比较致命的缺点:ABA问题,当两个线程都执行下面这段代码:

public final boolean getAndSet(boolean newValue) {
    boolean prev;
    do {
        prev = get();
    } while (!compareAndSet(prev, newValue));
    return prev;
}

当线程1执行到do时卡住,此时线程2将newValue值由true改为false,之后又改为true,这是线程1开始执行,发现get()到的value为true,就进行CAS,但是他不知道这个true已经被修改过了,有时我们不允许这样,就产生了AtomicStampedReference这个原子类,它的本质就是AtomicReference。

2、内部结构介绍

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);
    }
}

私有静态内部类,两个属性,泛型对象和版本号(类似于乐观锁)。

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

属性

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

构造方法。

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;
}

将stamp传给整形数组,并返回reference。

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

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) ||
         casPair(current, Pair.of(newReference, newStamp)));
}

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

解决ABA问题的关键就是compareAndSet,这里compareAndSet方法首先判断expectedReference和expectedStamp是否与current相等,这是因为它CAS操作的时候要使用地址,而传进来的参数是值,我们就需要根据值是否相等来转换为current地址,之后进行CAS操作。

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

无条件赋值

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

设置newStamp设置新版本号。

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

返回对象属性距离对象地址的偏移量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值