原子更新引用AtomicReference实现原理分析

1 前言

原子更新基本类型只能更新单个变量,而原子更新引用类型可以原子更新多个变量。Atomic包提供了以下3个类。

  • AtomicReference:原子更新引用类型。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  • AtomicMarkableReference:原子更新带有标记位的引用类型(可以原子更新一个布尔类型的标记位和引用类型。)

现在先来看看AtomicReference是如何实现的(基于JDK1.8)

2 实现过程分析

主要字段

private static final Unsafe unsafe = Unsafe.getUnsafe();//Unsafe所有并发工具的基础工具类
private static final long valueOffset; //成员变量value在当前AtomicReference对象中的地址偏移量

static {
    try {//使用objectFieldOffset获取偏移量
        valueOffset = unsafe.objectFieldOffset
            (AtomicReference.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

private volatile V value;//value表示我们要修改/更新的目标对象

AtomicReference内部有一个泛型成员变量value,此变量表示我们要修改/更新的目标对象,此属性是AtomicReference的核心。它被

volatile关键字修饰,使用Unsafe工具类能实时地修改/读取value的引用。

value 属性可以通过构造方法AtomicReference(V initialValue)初始化指定,还也可在利用set(V newValue)方法重新设定。

public AtomicReference(V initialValue) {
    value = initialValue;
}
public AtomicReference() {
}
public final void set(V newValue) {
    value = newValue;
}

get方法:返回当前最新的值

lazySet方法:最终设定指定值 (延迟化设值,不保证新值newValue被所有线程立即可见)

compareAndSetweakCompareAndSet均是CAS更新值(引用)。

getAndSet: 原子地设置新值并返回旧值(只有更新成功后方法才能返回)

    public final V get() {
        return value;
    }
    //最终设定 (延迟化设值,不保证新值newValue被所有立即可见)
    public final void lazySet(V newValue) {
        unsafe.putOrderedObject(this, valueOffset, newValue);
    }
    // CAS更新,(若expect==value,就将value设为update)并返回是否更新成功的布尔值
    public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }
    //同上的CAS,(方法定义上是对引用做弱比较,但实现细节和上面的compareAndSet相同)
    public final boolean weakCompareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

    //设置新值并返回旧值
    public final V getAndSet(V newValue) {
        return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
    }

    //Unsafe的getAndSetObject
    public final Object getAndSetObject(Object o, long offset, Object newValue) {
        Object v;
        do {
            v = getObjectVolatile(o, offset); //获取当前的值
        } while (!compareAndSwapObject(o, offset, v, newValue));//CAS更新
        return v;
    }

getAndUpdate 将单参数(实参为内部value)函数接口的返回值设为新值并返回旧值

updateAndGet 将单参数(实参为内部value)函数接口的返回值设为新值并返回新值

getAndAccumulate :将双参数(实参之一是内部value,另一个是显式指定的x)函数接口返回值作为新值并返回旧值

accumulateAndGet:将双参数(实参之一是内部value,另一个是x)函数接口返回值作为新值并返回新值

public final V getAndUpdate(UnaryOperator<V> updateFunction) {
    V prev, next;
    do {
        prev = get();//获取当前的value
        next = updateFunction.apply(prev); //updateFunction根据输入参数prev得到value新值
    } while (!compareAndSet(prev, next));//CAS更新
    return prev;
}
public final V updateAndGet(UnaryOperator<V> updateFunction) {
    V prev, next;
    do {
        prev = get();
        next = updateFunction.apply(prev);
    } while (!compareAndSet(prev, next));
    return next;
}
public final V getAndAccumulate(V x,
                                BinaryOperator<V> accumulatorFunction) {
    V prev, next;
    do {
        prev = get();//获取当前的value
        next = accumulatorFunction.apply(prev, x);//accumulatorFunction根据输入参数prev、x两者得到value新值
    } while (!compareAndSet(prev, next));//CAS更新
    return prev;
}
public final V accumulateAndGet(V x,
                                BinaryOperator<V> accumulatorFunction) {
    V prev, next;
    do {
        prev = get();
        next = accumulatorFunction.apply(prev, x);
    } while (!compareAndSet(prev, next));
    return next;
}

3 使用示例

public class AtomicRef {
    static class ID {
        private long serialNum;
        private String algo;
        private Date created;
        public ID(long serialNum, String algo, Date created) {
            this.serialNum = serialNum;
            this.algo = algo;
            this.created = created;
        }
//getter/setter省略
    }
    public static void main(String[] args) {
        AtomicReference<ID> idAtomicRef = new AtomicReference<>();

        ID id1 = new ID(1248L, "random", new Date());
        ID id2 = new ID(3456L, "random", new Date());

        idAtomicRef.set(id1);
        Boolean flag = idAtomicRef.compareAndSet(id1, id2);
        System.out.printf("CAS successful? %s,current ID:%s\n", flag, idAtomicRef);

        id1.setCreated(new Date(System.currentTimeMillis() +3600*1000));
        flag= idAtomicRef.compareAndSet(id2, id1);
        System.out.printf("CAS successful? %s,current ID:%s\n", flag, idAtomicRef);

        System.out.println(idAtomicRef);

        ID id3 = new ID(1024L, "Random", new Date());
        idAtomicRef.set(id3);

        ID id4 = idAtomicRef.updateAndGet(id -> new ID(id.getSerialNum() * 10, "scale", new Date()));

        ID id5 = idAtomicRef.accumulateAndGet(id1, (x, y) -> {
            long serialNum = x.serialNum + y.serialNum;
            long time = (x.getCreated().getTime() + y.getCreated().getTime()) / 2;
            return new ID(serialNum, "accumulate", new Date(time));
        });
        System.out.println(id4);
        System.out.println(id5);
    }
}

参考: Java并发编程的艺术》方腾飞

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值