Atomic之AtomicReference

5,985 阅读2分钟

AtomicReference的功能

从名字可以直观翻译出一句话: 提供了可以原子的读写对象引用的一种机制。

是AtomicReferenceXXX相关类的最基础类,还有AtomicReferenceArray、AtomicReferenceFieldUpdater、AtomicStampedReference等。

AtomicReference的接口列表(jdk1.8.0_151)

// 构造初始值为null的AtomicReference对象
public AtomicReference();

// 构造初始值为initialValue的AtomicReference对象
public AtomicReference(V initialValue)

// 获取当前value对象
public final V get()

// 直接更新volatile对象的值
public final void set(V newValue)

// 与set()方法的区别见: http://ifeve.com/juc-atomic-class-lazyset-que/
// https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6275329
// 虽然大神对使用场景做了介绍和说明,但是还是没有想出来什么样的场景会用到……-_-||
public final void lazySet(V newValue)

// 基于 == 操作进行的CAS更新
public final boolean compareAndSet(V expect, V update)

// 高效但不保证可见性的原子set操作,在无happens-before需求的场景下使用
public final boolean weakCompareAndSet(V expect, V update)

// 设置给定值,并返回旧值
public final V getAndSet(V newValue)

// 通过可重入无状态的方法更新对象值,并返回旧值
// 内部通过get + CAS自旋的策略进行
public final V getAndUpdate(UnaryOperator<V> updateFunction)

// 与上一个方法类似,只是返回更新后的新值
public final V updateAndGet(UnaryOperator<V> updateFunction)

// 回调方法可以带入参的更新操作,返回旧值
// V x作为accumulatorFunction的一个入参,accumulatorFunction.apply(prev, x)
public final V getAndAccumulate(V x,BinaryOperator<V> accumulatorFunction)

// 与上一个方法类似,返回新值
public final V accumulateAndGet(V x,BinaryOperator<V> accumulatorFunction)

关键点

  1. 使用CAS操作保证原子性, 使用volatile修饰保证可见性;
  2. 相等的比较算法是 ==,即基本数据类型判断值,对象类型判断引用地址是否相同;
public class AtomicReferenceTest {

    public static void main(String[] args) {

        String value = "hello world";
        ReferenceObject object = new AtomicReferenceTest.ReferenceObject(value);
        AtomicReference<ReferenceObject> atomicReference = new AtomicReference(object);
        atomicReference.compareAndSet(object, new ReferenceObject("Hello guy!"));
        System.out.println(atomicReference.get().getValue());

        // 顺道测试一下Integer的常量池
        AtomicReference<Integer> integerNewReference = new AtomicReference(new Integer(10));
        integerNewReference.compareAndSet(new Integer(10), new Integer(-10));
        System.out.println(integerNewReference.get().intValue());

        AtomicReference<Integer> integerValeOfReference = new AtomicReference(Integer.valueOf(10));
        integerValeOfReference.compareAndSet(new Integer(10), new Integer(-10));
        System.out.println(integerValeOfReference.get().intValue());

        integerValeOfReference.compareAndSet(Integer.valueOf(10), new Integer(-10));
        System.out.println(integerValeOfReference.get().intValue());
    }

    static class ReferenceObject {

        private String value;

        public ReferenceObject(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }
}

可以插播Integer常量池的特点, new Integer是在堆上生成一个新对象, 只有Integer对象赋值(编译时转成Integer.valueOf())或Integer.valueof且默认数值在-128 - 127才会用到常量池.

扩展

AtomicReferenceArray: 初始化时计算数组的起始位置和每个元素的偏移量,通过base + i << shift进行数组索引;

AtomicReferenceFieldUpdater: 对指定类中的volatile属性进行原子更新,一般使用不会存在问题。但如果涉及自定义类加载器的场景,需要注意需保证在同一组类加载器加载中。