【Java源码阅读系列19】深度解读Java AtomicReference 源码

AtomicReferenceJava java.util.concurrent.atomic 包中的核心类之一,用于在多线程环境下实现线程安全的对象引用原子操作。本文将结合 JDK 8 源码,从底层实现、核心方法、设计模式等角度深入解析其原理。

一、类定义与核心字段

1. 类泛型与继承

AtomicReference 是泛型类( 表示引用对象的类型),实现了 java.io.Serializable(支持序列化):

public class AtomicReference<V> implements java.io.Serializable {
    // ... 核心字段与方法 ...
}

2. 核心字段:UnsafeCAS 基础

AtomicReference 的原子性依赖以下核心字段:

  • private static final Unsafe unsafe:通过 Unsafe.getUnsafe() 获取的底层操作工具类,提供硬件级别的原子操作(如 CAS)。
  • private static final long valueOffset:通过 Unsafe.objectFieldOffset 反射获取的 value 字段在内存中的偏移量(用于 CAS 操作定位内存地址)。
  • private volatile V value:实际存储对象引用的变量,volatile 保证多线程间的可见性(修改后立即被其他线程感知)。
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
    try {
        // 反射获取 value 字段的内存偏移量
        valueOffset = unsafe.objectFieldOffset(AtomicReference.class.getDeclaredField("value"));
    } catch (Exception ex) { 
        throw new Error(ex); 
    }
}

private volatile V value; // 实际存储的对象引用,volatile 保证可见性

二、核心方法解析:对象引用的原子操作

AtomicReference 的原子性通过 CASCompare-And-Swap)算法实现,其逻辑与 AtomicInteger 类似,但操作对象是对象引用而非基本类型。

1. 基础操作:get/set

  • get():直接返回 value 的当前引用(依赖volatile 保证可见性)。
  • set(V newValue):直接修改 value(同样依赖 volatile 保证可见性)。
  • lazySet(V newValue):通过 Unsafe.putOrderedObject 实现延迟设置,不保证立即对其他线程可见(适用于非关键的最终一致性场景)。

2. 原子更新操作:CAS 直接调用

compareAndSet(V expect, V update) 是核心原子方法,直接调用 Unsafe.compareAndSwapObject 实现 CAS

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
  • 参数expect 是预期的旧引用,update 是目标新引用。
  • 返回值:更新成功返回 true,否则 false(说明有其他线程修改了引用)。

3. 原子替换与获取:getAndSet

getAndSet(V newValue) 原子性地设置新值并返回旧值,底层调用 Unsafe.getAndSetObject

@SuppressWarnings("unchecked")
public final V getAndSet(V newValue) {
    return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
}

Unsafe.getAndSetObject 的底层逻辑是循环执行 CAS,直到成功替换引用。

4. JDK8 新增:函数式原子更新

JDK8 引入了 UnaryOperatorBinaryOperator 函数式接口,支持自定义原子更新逻辑,例如 getAndUpdate

public final V getAndUpdate(UnaryOperator<V> updateFunction) {
    V prev, next;
    do {
        prev = get(); // 获取当前引用
        next = updateFunction.apply(prev); // 应用自定义函数计算新引用
    } while (!compareAndSet(prev, next)); // 循环 CAS 直到成功
    return prev; // 返回旧引用
}
  • 设计思想:将原子操作的骨架(循环 CAS)固定,具体的更新逻辑由用户传入的函数决定,符合模板方法模式(Template Method Pattern)。

三、设计模式分析

AtomicReference的实现中隐含了两种关键设计模式:

1. 代理模式(Proxy Pattern)

AtomicReference 的大部分原子方法(如 compareAndSetgetAndSet)本质上是对 Unsafe 类对应方法的代理。Unsafe 提供了底层的 CAS 操作,而 AtomicReference 封装了这些操作,提供更易用的业务接口。

优点:隔离底层实现细节,降低用户使用复杂度。

2. 模板方法模式(Template Method Pattern)

JDK8 新增的函数式方法(如 getAndUpdateaccumulateAndGet)定义了原子操作的骨架流程(循环获取当前引用 → 计算新引用 → CAS 更新),具体的计算逻辑由用户传入的函数式接口实现。

优点:灵活扩展原子操作的逻辑,同时保证原子性的核心流程不变。


四、CAS 的优缺点与适用场景

优点

  • 无锁高效:相比 synchronized 的阻塞机制,CAS 是乐观锁,避免了线程上下文切换开销。
  • 轻量级原子性:适用于短时间、低竞争的对象引用原子更新(如状态标志、缓存条目替换)。

缺点

  • ABA 问题:若一个引用从 A→B→ACAS 会误判为未修改。可通过 AtomicStampedReference(带版本号)或 AtomicMarkableReference(带标记)解决。
  • 高竞争失效:在高并发场景下,CAS 可能频繁重试,导致性能下降(此时 synchronized 可能更优)。

五、总结

AtomicReference 是 Java 并发编程中实现对象引用原子操作的核心工具,其设计思想可总结为:
通过 volatile 保证可见性 + Unsafe 提供的 CAS 保证原子性,结合代理模式和模板方法模式,封装出易用、高效的线程安全对象引用操作类。理解其源码有助于深入掌握 Java 并发编程的底层逻辑,以及无锁算法在对象引用场景中的应用。

你可能感兴趣的:(源码阅读系列之Java,java,开发语言,jvm)