java.util.concurrent.atomic 包下的 Atomic 类

13 阅读17分钟

java.util.concurrent.atomic 包提供了一组基于 CAS(Compare-And-Swap)  操作的线程安全类,用于在多线程环境中以原子方式操作变量。这些类通过硬件级别的原子操作(CAS)实现线程安全,避免了传统锁机制的开销。


1. Atomic 类的分类

java.util.concurrent.atomic 包下的主要 Atomic 类可以分为以下几类:

1.1 基本类型的 Atomic 类
  • AtomicBoolean:用于以原子方式操作布尔值(boolean)。
  • AtomicInteger:用于以原子方式操作整数(int)。
  • AtomicLong:用于以原子方式操作长整数(long)。
1.2 引用类型的 Atomic 类
  • AtomicReference<V> :用于以原子方式操作引用类型的变量。
  • AtomicStampedReference<V> :用于以原子方式操作引用类型的变量,同时解决 CAS 的 ABA 问题。
  • AtomicMarkableReference<V> :用于以原子方式操作引用类型的变量,并附带一个布尔标志位。
1.3 数组类型的 Atomic 类
  • AtomicIntegerArray:用于以原子方式操作 int 类型的数组。
  • AtomicLongArray:用于以原子方式操作 long 类型的数组。
  • AtomicReferenceArray<E> :用于以原子方式操作引用类型的数组。
1.4 累加器和计数器
  • LongAdder 和 DoubleAdder:用于高效地累加 long 和 double 类型的值,适合高并发场景。
  • LongAccumulator 和 DoubleAccumulator:支持自定义累加函数的高效累加器。

2. 常用 Atomic 类及其方法

以下是每个常用 Atomic 类的详细介绍,包括常用方法及其原子性保证原理。


2.1 AtomicBoolean

作用:以原子方式操作布尔值(boolean)。

常用方法

  1. get() :获取当前值。

  2. set(boolean newValue) :设置新值。

  3. compareAndSet(boolean expect, boolean update)

    • 如果当前值等于 expect,则更新为 update,否则不更新。
  4. getAndSet(boolean newValue)

    • 将值设置为 newValue,并返回旧值。

原子性保证

  • 通过底层的 CAS 操作(compareAndSwapInt)实现。
  • CAS 操作依赖于硬件指令(如 x86 的 CMPXCHG 或 ARM 的 LDREX/STREX),能够在硬件层面保证比较和更新操作的原子性。

2.2 AtomicInteger

作用:以原子方式操作整数(int)。

常用方法

  1. get() :获取当前值。

  2. set(int newValue) :设置新值。

  3. compareAndSet(int expect, int update)

    • 如果当前值等于 expect,则更新为 update,否则不更新。
  4. getAndIncrement()

    • 将当前值加 1,并返回旧值。
  5. incrementAndGet()

    • 将当前值加 1,并返回新值。
  6. getAndAdd(int delta)

    • 将当前值加上 delta,并返回旧值。
  7. addAndGet(int delta)

    • 将当前值加上 delta,并返回新值。

原子性保证

  • 通过底层的 CAS 操作(compareAndSwapInt)实现。
  • 如果 CAS 操作失败,方法会进入自旋状态,直到更新成功。

2.3 AtomicLong

作用:以原子方式操作长整数(long)。

常用方法

  1. get() :获取当前值。

  2. set(long newValue) :设置新值。

  3. compareAndSet(long expect, long update)

    • 如果当前值等于 expect,则更新为 update,否则不更新。
  4. getAndIncrement()

    • 将当前值加 1,并返回旧值。
  5. incrementAndGet()

    • 将当前值加 1,并返回新值。
  6. getAndAdd(long delta)

    • 将当前值加上 delta,并返回旧值。
  7. addAndGet(long delta)

    • 将当前值加上 delta,并返回新值。

原子性保证

  • 与 AtomicInteger 类似,依赖底层的 CAS 操作(compareAndSwapLong)。

2.4 AtomicReference<V>

作用:以原子方式操作引用类型的变量。

常用方法

  1. get() :获取当前引用。

  2. set(V newValue) :设置新引用。

  3. compareAndSet(V expect, V update)

    • 如果当前引用等于 expect,则更新为 update,否则不更新。
  4. getAndSet(V newValue)

    • 将引用设置为 newValue,并返回旧引用。

原子性保证

  • 通过底层的 CAS 操作(compareAndSwapObject)实现。

2.5 AtomicStampedReference<V>

作用:以原子方式操作引用类型的变量,同时解决 CAS 的 ABA 问题。

常用方法

  1. getReference() :获取当前引用。

  2. getStamp() :获取当前版本号(Stamp)。

  3. compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)

    • 如果当前引用等于 expectedReference 且版本号等于 expectedStamp,则更新为 newReference 和 newStamp
  4. get()

    • 同时获取引用和版本号。

原子性保证

  • 在引用的基础上增加了版本号,通过版本号解决 ABA 问题。

2.6 AtomicIntegerArray

作用:以原子方式操作 int 类型的数组。

常用方法

  1. get(int index) :获取指定索引的值。

  2. set(int index, int newValue) :设置指定索引的值。

  3. compareAndSet(int index, int expect, int update)

    • 如果指定索引的值等于 expect,则更新为 update
  4. getAndIncrement(int index)

    • 将指定索引的值加 1,并返回旧值。

原子性保证

  • 每个数组元素的操作通过 CAS 实现,保证线程安全。

3. 原子性保证的原理

1. CAS(Compare-And-Swap)
  • 核心思想

    • 比较内存中的值是否等于预期值(expected)。
    • 如果相等,则将内存中的值更新为新值(update)。
    • 如果不相等,则不更新,并返回失败。
  • 硬件支持

    • CAS 操作依赖于 CPU 提供的原子指令(如 x86 的 CMPXCHG 或 ARM 的 LDREX/STREX)。
    • 这些指令能够在硬件层面保证比较和更新操作的原子性。
2. 自旋重试
  • 如果 CAS 操作失败,Atomic 类的方法会进入自旋状态,重新尝试,直到操作成功。
3. 不可分割性
  • CAS 操作是不可分割的,能够在多线程环境下保证数据的一致性。

4. 优缺点

优点
  1. 线程安全

    • 通过 CAS 操作实现线程安全,无需显式加锁。
  2. 高性能

    • 避免了传统锁机制的开销(如线程阻塞和上下文切换)。
  3. 简单易用

    • 提供了原子操作的封装,API 简单。
缺点
  1. 自旋开销

    • 如果 CAS 操作失败,可能导致自旋重试,浪费 CPU 资源。
  2. ABA 问题

    • 在某些场景下,CAS 操作可能会遇到 ABA 问题,需要额外处理(如使用 AtomicStampedReference)。

5. 总结

  • java.util.concurrent.atomic 包的作用

    • 提供了一组线程安全的类,用于以原子方式操作变量。
  • 常用类

    • AtomicBooleanAtomicIntegerAtomicLongAtomicReference 等。
  • 原子性保证

    • 通过底层的 CAS 操作和自旋重试机制实现。
  • 适用场景

    • 高并发环境下的计数器、标志位、引用更新等。

getAndSet 方法是如何保证原子性的

getAndSet 方法的原子性保证

getAndSet 是 Java 并发包中 Atomic 类(如 AtomicBooleanAtomicIntegerAtomicReference 等)提供的一个方法,用于以原子方式将变量设置为新值,并返回设置前的旧值。

方法定义

以 AtomicBoolean 为例,getAndSet 方法的定义如下:

public final boolean getAndSet(boolean newValue) {
    boolean prev;
    do {
        prev = get(); // 获取当前值
    } while (!compareAndSet(prev, newValue)); // CAS 操作
    return prev; // 返回旧值
}

getAndSet 的作用

  • 功能

    • 将变量的值设置为 newValue
    • 返回设置前的旧值。
  • 线程安全

    • 通过底层的 CAS(Compare-And-Swap)  操作保证线程安全。
  • 原子性

    • 整个操作(获取旧值并设置新值)是不可分割的,多个线程同时调用时不会出现竞态条件。

2. getAndSet 的实现原理

1. 基于 CAS 实现

getAndSet 的核心是通过 CAS(Compare-And-Swap)  操作实现的。CAS 是一种硬件级别的原子操作,能够在多线程环境下安全地比较和更新变量的值。

2. 工作流程

以下是 getAndSet 的工作流程:

  1. 获取当前值

    • 调用 get() 方法读取当前值。
  2. 尝试更新值

    • 使用 compareAndSet 方法尝试将当前值更新为新值。
    • 如果当前值未被其他线程修改,则更新成功。
    • 如果当前值已被其他线程修改,则重试。
  3. 返回旧值

    • 当 CAS 操作成功时,返回旧值。
3. 自旋重试
  • 如果 CAS 操作失败(即当前值已被其他线程修改),getAndSet 会进入自旋状态,重新获取当前值并重试,直到更新成功。

3. getAndSet 的原子性保证

1. CAS 的原子性
  • CAS 操作依赖于底层硬件指令(如 x86 架构的 CMPXCHG 指令或 ARM 架构的 LDREX/STREX 指令),这些指令能够在硬件层面保证比较和更新操作的原子性。
  • 在多线程环境下,CAS 操作可以确保只有一个线程能够成功更新变量的值。
2. 自旋重试机制
  • 如果 CAS 操作失败,getAndSet 会通过自旋重试的方式确保操作最终成功。

  • 自旋重试的逻辑如下:

    • 获取当前值。
    • 尝试更新值。
    • 如果失败,则重新获取当前值并重试。
3. 不可分割性
  • getAndSet 的整个操作(获取旧值并设置新值)是不可分割的。
  • 即使多个线程同时调用 getAndSet,每个线程都会看到一个一致的旧值,并成功更新变量的值。

4. 示例代码

以下是一个使用 AtomicBoolean 的 getAndSet 示例:

import java.util.concurrent.atomic.AtomicBoolean;

public class GetAndSetExample {
    public static void main(String[] args) {
        AtomicBoolean atomicBoolean = new AtomicBoolean(false);

        // 使用 getAndSet 方法
        boolean oldValue = atomicBoolean.getAndSet(true);

        System.out.println("Old Value: " + oldValue); // 输出: false
        System.out.println("New Value: " + atomicBoolean.get()); // 输出: true
    }
}
执行流程
  1. 初始值为 false

  2. 调用 getAndSet(true)

    • 获取当前值 false
    • 将值更新为 true
    • 返回旧值 false
  3. 最终值为 true


5. 多线程场景中的行为

以下是一个多线程场景下使用 getAndSet 的示例:

import java.util.concurrent.atomic.AtomicBoolean;

public class MultiThreadGetAndSet {
    private static final AtomicBoolean flag = new AtomicBoolean(false);

    public static void main(String[] args) {
        Runnable task = () -> {
            boolean oldValue = flag.getAndSet(true);
            System.out.println(Thread.currentThread().getName() + " - Old Value: " + oldValue);
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();
    }
}
可能的输出
Thread-0 - Old Value: false
Thread-1 - Old Value: true
解释
  1. 初始值为 false

  2. 两个线程同时调用 getAndSet(true)

    • 第一个线程成功更新值,将值从 false 更新为 true,并返回旧值 false
    • 第二个线程读取到更新后的值 true,并返回旧值 true

6. getAndSet 的优缺点

优点
  1. 线程安全

    • 通过 CAS 操作保证线程安全,无需显式加锁。
  2. 高性能

    • 避免了传统锁机制的开销(如线程阻塞和上下文切换)。
  3. 简单易用

    • 提供了原子操作的封装,API 简单。
缺点
  1. 自旋开销

    • 如果 CAS 操作失败,getAndSet 会进入自旋状态,可能导致 CPU 资源浪费。
  2. 适用场景有限

    • 仅适用于单个变量的原子更新,无法直接操作多个变量。

7. getAndSet 的底层实现

1. Unsafe 类的支持
  • getAndSet 的底层实现依赖于 Unsafe 类的 compareAndSwapXXX 方法。
  • 示例(以 AtomicBoolean 为例):
public final boolean getAndSet(boolean newValue) {
    boolean prev;
    do {
        prev = get(); // 获取当前值
    } while (!compareAndSet(prev, newValue)); // CAS 操作
    return prev; // 返回旧值
}
2. 硬件指令的支持
  • compareAndSet 方法通过 JNI 调用底层硬件指令(如 x86 的 CMPXCHG 或 ARM 的 LDREX/STREX)实现原子性。

8. 总结

  1. getAndSet 的作用

    • 以原子方式将变量设置为新值,并返回旧值。
  2. 原子性保证

    • 通过底层的 CAS 操作和自旋重试机制实现。
  3. 适用场景

    • 适用于需要以线程安全的方式更新单个变量的场景。
  4. 优缺点

    • 优点:线程安全、高性能、简单易用。
    • 缺点:自旋可能导致 CPU 资源浪费,适用场景有限。

AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater

AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 是 Java 并发包(java.util.concurrent.atomic)中的两个工具类,用于以原子方式更新对象中指定的 int 或 long 类型的字段。这些类通过反射机制操作字段,并且可以避免直接使用锁来实现线程安全。


1. 为什么需要 AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater

  1. 节省内存

    • 如果直接使用 AtomicInteger 或 AtomicLong,需要为每个字段创建一个独立的对象。
    • 使用 AtomicIntegerFieldUpdater 或 AtomicLongFieldUpdater 可以直接操作对象的字段,避免额外的对象开销。
  2. 高效更新

    • 它们通过 CAS(Compare-And-Swap)操作实现线程安全,性能高于传统的锁机制。
  3. 灵活性

    • 允许对现有类的字段进行原子操作,而无需修改类的定义(如将字段改为 AtomicInteger)。

2. 使用限制

  1. 字段必须是 volatile 修饰的

    • 为了确保字段的可见性,AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 要求被操作的字段必须用 volatile 修饰。
  2. 字段不能是 static 的

    • 这两个类只能操作实例字段,不能操作静态字段。
  3. 字段的访问权限

    • 如果字段是 private 或 protected,需要通过反射访问,调用者必须有足够的权限(如在同一个包中或通过设置 Accessible)。

3. AtomicIntegerFieldUpdater 的使用

定义

AtomicIntegerFieldUpdater 是一个工具类,用于以原子方式更新对象中 int 类型的字段。

常用方法
  1. newUpdater(Class<T> tclass, String fieldName)

    • 创建一个 AtomicIntegerFieldUpdater 实例。

    • 参数:

      • tclass:包含目标字段的类。
      • fieldName:目标字段的名称。
  2. get(T obj)

    • 获取目标对象中字段的当前值。
  3. set(T obj, int newValue)

    • 设置目标对象中字段的新值。
  4. compareAndSet(T obj, int expect, int update)

    • 如果字段的当前值等于 expect,则将其更新为 update
  5. getAndIncrement(T obj)

    • 将字段的值加 1,并返回旧值。
  6. incrementAndGet(T obj)

    • 将字段的值加 1,并返回新值。
示例代码
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

class Person {
    volatile int age; // 必须是 volatile 修饰的字段

    public Person(int age) {
        this.age = age;
    }
}

public class AtomicIntegerFieldUpdaterExample {
    public static void main(String[] args) {
        // 创建一个 AtomicIntegerFieldUpdater 实例
        AtomicIntegerFieldUpdater<Person> updater =
                AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");

        Person person = new Person(25);

        // 获取当前值
        System.out.println("Initial Age: " + updater.get(person)); // 输出: 25

        // 更新值
        updater.set(person, 30);
        System.out.println("Updated Age: " + updater.get(person)); // 输出: 30

        // CAS 操作
        boolean success = updater.compareAndSet(person, 30, 35);
        System.out.println("CAS Success: " + success); // 输出: true
        System.out.println("Age after CAS: " + updater.get(person)); // 输出: 35

        // 自增操作
        int oldAge = updater.getAndIncrement(person);
        System.out.println("Old Age: " + oldAge); // 输出: 35
        System.out.println("New Age: " + updater.get(person)); // 输出: 36
    }
}

4. AtomicLongFieldUpdater 的使用

定义

AtomicLongFieldUpdater 是一个工具类,用于以原子方式更新对象中 long 类型的字段。

常用方法

与 AtomicIntegerFieldUpdater 类似,AtomicLongFieldUpdater 提供的方法包括:

  1. newUpdater(Class<T> tclass, String fieldName)

    • 创建一个 AtomicLongFieldUpdater 实例。
  2. get(T obj)

    • 获取目标对象中字段的当前值。
  3. set(T obj, long newValue)

    • 设置目标对象中字段的新值。
  4. compareAndSet(T obj, long expect, long update)

    • 如果字段的当前值等于 expect,则将其更新为 update
  5. getAndIncrement(T obj)

    • 将字段的值加 1,并返回旧值。
  6. incrementAndGet(T obj)

    • 将字段的值加 1,并返回新值。
示例代码
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

class Account {
    volatile long balance; // 必须是 volatile 修饰的字段

    public Account(long balance) {
        this.balance = balance;
    }
}

public class AtomicLongFieldUpdaterExample {
    public static void main(String[] args) {
        // 创建一个 AtomicLongFieldUpdater 实例
        AtomicLongFieldUpdater<Account> updater =
                AtomicLongFieldUpdater.newUpdater(Account.class, "balance");

        Account account = new Account(1000L);

        // 获取当前值
        System.out.println("Initial Balance: " + updater.get(account)); // 输出: 1000

        // 更新值
        updater.set(account, 1500L);
        System.out.println("Updated Balance: " + updater.get(account)); // 输出: 1500

        // CAS 操作
        boolean success = updater.compareAndSet(account, 1500L, 2000L);
        System.out.println("CAS Success: " + success); // 输出: true
        System.out.println("Balance after CAS: " + updater.get(account)); // 输出: 2000

        // 自增操作
        long oldBalance = updater.getAndIncrement(account);
        System.out.println("Old Balance: " + oldBalance); // 输出: 2000
        System.out.println("New Balance: " + updater.get(account)); // 输出: 2001
    }
}

5. 原子性保证的原理

1. CAS(Compare-And-Swap)
  • AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 的核心是 CAS 操作。
  • CAS 操作通过底层硬件指令(如 x86 的 CMPXCHG 或 ARM 的 LDREX/STREX)实现,能够在硬件层面保证比较和更新操作的原子性。
2. 反射机制
  • 这两个类通过反射机制访问目标对象的字段。
  • 在创建 Updater 时,newUpdater 方法会检查字段是否符合要求(如是否为 volatile),并通过反射获取字段的内存偏移量。
3. 内存偏移量
  • Updater 使用 Unsafe 类的 compareAndSwapXXX 方法,通过字段的内存偏移量直接操作内存,避免了传统的锁机制。

6. 优缺点

优点
  1. 节省内存

    • 避免了为每个字段创建独立的 AtomicInteger 或 AtomicLong 对象。
  2. 高性能

    • 通过 CAS 操作实现线程安全,性能高于传统锁机制。
  3. 灵活性

    • 可以对现有类的字段进行原子操作,而无需修改类的定义。
缺点
  1. 使用限制

    • 字段必须是 volatile 修饰的,且不能是 static
  2. 复杂性

    • 需要通过反射访问字段,可能导致代码复杂度增加。
  3. 安全性

    • 如果字段是 private 或 protected,需要确保调用者有足够的权限。

7. 总结

  • AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 的作用

    • 提供了一种高效的方式,以原子方式更新对象中的 int 或 long 类型字段。
  • 适用场景

    • 高并发环境下的计数器、状态标志等。
  • 原子性保证

    • 通过 CAS 操作和反射机制实现。
  • 优缺点

    • 节省内存、高性能,但使用限制较多。

AtomicReferenceAtomicStampedReference 和 AtomicMarkableReference 的详细介绍

这三个类都属于 java.util.concurrent.atomic 包,主要用于以原子方式操作引用类型的变量。它们的核心功能是基于 CAS(Compare-And-Swap)  操作实现线程安全,但它们的功能和适用场景有所不同。


1. AtomicReference

定义
  • AtomicReference<V>  是一个支持原子操作的类,用于操作引用类型的变量(如对象)。
  • 它允许在多线程环境中安全地更新对象引用,而无需显式加锁。
常用方法
  1. get()
  • 获取当前引用的值。
  • 示例:
AtomicReference<String> atomicReference = new AtomicReference<>("Initial");
System.out.println(atomicReference.get()); // 输出: Initial
  1. set(V newValue)
  • 将引用设置为指定的新值。
  • 示例:
atomicReference.set("Updated");
System.out.println(atomicReference.get()); // 输出: Updated
  1. compareAndSet(V expectedValue, V newValue)
  • 如果当前引用等于 expectedValue,则将其更新为 newValue,否则不更新。
  • 示例:
boolean result = atomicReference.compareAndSet("Initial", "New Value");
System.out.println(result); // 输出: false
System.out.println(atomicReference.get()); // 输出: Updated
  1. getAndSet(V newValue)
  • 将引用设置为 newValue,并返回设置前的旧值。
  • 示例:
String oldValue = atomicReference.getAndSet("Final Value");
System.out.println(oldValue); // 输出: Updated
System.out.println(atomicReference.get()); // 输出: Final Value
适用场景
  • 用于简单的引用更新操作。
  • 适合没有 ABA 问题 的场景。
缺点
  • 无法解决 CAS 的 ABA 问题(即值从 A 变为 B,然后又变回 A,CAS 操作会认为值没有变化)。

2. AtomicStampedReference

定义
  • AtomicStampedReference<V>  是 AtomicReference 的扩展,除了存储引用值外,还存储一个版本号(Stamp)
  • 它主要用于解决 CAS 操作中的 ABA 问题
常用方法
  1. getReference()
  • 获取当前引用的值。
  • 示例:
AtomicStampedReference<String> atomicStampedRef = new AtomicStampedReference<>("Initial", 1);
System.out.println(atomicStampedRef.getReference()); // 输出: Initial
  1. getStamp()
  • 获取当前的版本号(Stamp)。
  • 示例:
System.out.println(atomicStampedRef.getStamp()); // 输出: 1
  1. compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
  • 如果当前引用等于 expectedReference 且版本号等于 expectedStamp,则将引用更新为 newReference,版本号更新为 newStamp
  • 示例:
boolean result = atomicStampedRef.compareAndSet("Initial", "Updated", 1, 2);
System.out.println(result); // 输出: true
System.out.println(atomicStampedRef.getReference()); // 输出: Updated
System.out.println(atomicStampedRef.getStamp()); // 输出: 2
  1. get(int[] stampHolder)
  • 同时获取引用值和版本号。
  • 示例:
int[] stampHolder = new int[1];
String value = atomicStampedRef.get(stampHolder);
System.out.println("Value: " + value + ", Stamp: " + stampHolder[0]); // 输出: Value: Updated, Stamp: 2
适用场景
  • 用于需要解决 ABA 问题 的场景。
  • 例如:在多线程环境中,某个变量的值可能被其他线程多次修改并恢复为原值,导致 CAS 操作误判。
缺点
  • 增加了版本号的管理,使用复杂度较高。

3. AtomicMarkableReference

定义
  • AtomicMarkableReference<V>  是 AtomicReference 的扩展,除了存储引用值外,还存储一个布尔标志位(Mark)
  • 它主要用于标记引用的状态。
常用方法
  1. getReference()
  • 获取当前引用的值。
  • 示例:
AtomicMarkableReference<String> atomicMarkableRef = new AtomicMarkableReference<>("Initial", false);
System.out.println(atomicMarkableRef.getReference()); // 输出: Initial
  1. isMarked()
  • 获取当前的标志位(Mark)。
  • 示例:
System.out.println(atomicMarkableRef.isMarked()); // 输出: false
  1. compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)
  • 如果当前引用等于 expectedReference 且标志位等于 expectedMark,则将引用更新为 newReference,标志位更新为 newMark
  • 示例:
boolean result = atomicMarkableRef.compareAndSet("Initial", "Updated", false, true);
System.out.println(result); // 输出: true
System.out.println(atomicMarkableRef.getReference()); // 输出: Updated
System.out.println(atomicMarkableRef.isMarked()); // 输出: true
  1. get(boolean[] markHolder)
  • 同时获取引用值和标志位。
  • 示例:
boolean[] markHolder = new boolean[1];
String value = atomicMarkableRef.get(markHolder);
System.out.println("Value: " + value + ", Mark: " + markHolder[0]); // 输出: Value: Updated, Mark: true
适用场景
  • 用于需要附加状态标记的场景。
  • 例如:在并发环境中,标志位可以用来表示某个引用是否被逻辑删除。
缺点
  • 增加了标志位的管理,使用复杂度较高。

4. 三者的对比

特性AtomicReferenceAtomicStampedReferenceAtomicMarkableReference
存储内容引用值引用值 + 版本号(Stamp)引用值 + 标志位(Mark)
解决 ABA 问题无法解决通过版本号解决无法解决
附加状态版本号布尔标志位
适用场景简单的引用更新需要解决 ABA 问题的场景需要附加状态标记的场景
使用复杂度简单较复杂较复杂

5. 示例场景

1. AtomicReference 的场景
  • 简单的引用更新操作。
  • 示例:实现线程安全的单例模式。
public class Singleton {
    private static final AtomicReference<Singleton> instance = new AtomicReference<>();

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance.get() == null) {
            instance.compareAndSet(null, new Singleton());
        }
        return instance.get();
    }
}
2. AtomicStampedReference 的场景
  • 解决 ABA 问题。
  • 示例:模拟 ABA 问题的解决。
public class ABAProblem {
    public static void main(String[] args) {
        AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 1);

        Thread t1 = new Thread(() -> {
            int stamp = ref.getStamp();
            ref.compareAndSet("A", "B", stamp, stamp + 1);
            ref.compareAndSet("B", "A", stamp + 1, stamp + 2);
        });

        Thread t2 = new Thread(() -> {
            int stamp = ref.getStamp();
            boolean result = ref.compareAndSet("A", "C", stamp, stamp + 1);
            System.out.println("Update success: " + result); // 输出: false
        });

        t1.start();
        t2.start();
    }
}
3. AtomicMarkableReference 的场景
  • 附加状态标记。
  • 示例:逻辑删除。
public class LogicalDeletion {
    public static void main(String[] args) {
        AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A", false);

        // 标记为逻辑删除
        ref.compareAndSet("A", "A", false, true);
        System.out.println("Is marked: " + ref.isMarked()); // 输出: true
    }
}

6. 总结

  • AtomicReference

    • 用于简单的引用更新操作。
    • 无法解决 ABA 问题。
  • AtomicStampedReference

    • 在引用更新的基础上增加了版本号,用于解决 ABA 问题。
  • AtomicMarkableReference

    • 在引用更新的基础上增加了布尔标志位,用于附加状态标记。