Jdk源码分析

文章40 |   阅读 14786 |   点赞0

来源:https://yumbo.blog.csdn.net/category_10384063.html

AtomicStampedReference的源码

x33g5p2x  于2021-12-18 转载在 其他  
字(2.7k)|赞(0)|评价(0)|浏览(330)

在我们使用CAS操作的时候会有一个问题那就是CAS过程中,预期值可能被更新了多次,最终又更新会预期值,这样判断cas操作则是成功的因为本次cas操作符合条件。

AtomicStampedReference总的来说就是解决一个线程将A改成C,也就是说当前值是A。
但是会出现A被改成B后又改回A,那么A改成C还是成功的。有些时候是不允许这种情况。因此有了AtomicStampedReference类,它在CAS的基础上加了一个时间戳的概念,实际上就是在原先需要预期值和新值,两个参数,现在需要另外加两个参数也就是预期版本号、更新后的版本号。如果预期值或预期版本号其中一个不符合就会更新失败。

常用的方法:

  1. 更新操作,需要传入4个参数:weakCompareAndSet()或者compareAndSet()两者效果一致
  2. 获取当前值,不需要参数getReference()
  3. 获取当前版本号,不需要参数getStamp()
  4. 更新时间戳attemptStamp(预期版本号 , 新版本号)
  5. 无条件更新当前值和当前版本号set( 新值, 新版本号 )
  6. 返回当前值和当前版本号get(int[] stampHolder)当前值通过返回值得到,当前版本号则在stampHolder[0]

另外一个相识的类 AtomicMarkableReference

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

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

        // 得到一个Pair实例
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    private volatile Pair<V> pair;
    private static final VarHandle PAIR;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            PAIR = l.findVarHandle(AtomicStampedReference.class, "pair", Pair.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private boolean casPair(Pair<V> cmp, Pair<V> val) {
        return PAIR.compareAndSet(this, cmp, val);
    }

    /** * 创建一个有初始值和初始版本号的AtomicStampedReference实例 */
    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;
    }

    /** * cas操作,多个期望的时间戳和更新后的时间戳两个参数 */
    public boolean weakCompareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) {
        return compareAndSet(expectedReference, newReference, expectedStamp, newStamp);
    }

    /** * cas操作被上面方法调用,意味着两个方法效果一致 */
    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)));
    }

    /** * 无条件的更新当前值和当前时间戳 */
    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)));
    }

}

相关文章