原子操作类详解

173 阅读4分钟

java.util.concurrent.atomic 包)提供了一种无锁、线程安全的方式来操作共享变量,底层基于 CAS(Compare and Swap) 机制实现。它们适用于高并发场景下的简单原子操作,避免了传统锁机制的性能开销。主要作用如下:

  1. 线程安全:保证对变量的操作在多线程环境下是原子的。
  2. 高性能:通过 CAS 无锁机制减少线程阻塞和上下文切换。
  3. 简化代码:避免显式使用 synchronizedLock

一、基本类型原子类

1. AtomicBoolean

特性:原子更新布尔值。 • 实现原理:内部通过volatile int value存储状态(0表示false,1表示true),使用Unsafe类的CAS操作保证原子性。 • 核心方法

public final boolean compareAndSet(boolean expect, boolean update);
2. AtomicInteger

特性:原子更新整型值。 • 实现原理:内部维护volatile int value,通过Unsafe的CAS操作(如getAndAddInt)实现原子增减。 • 典型方法

public final int incrementAndGet(); // 原子自增
public final int getAndAdd(int delta); // 原子加delta
3. AtomicLong

特性:原子更新长整型值。 • 实现原理:与AtomicInteger类似,使用volatile long valueUnsafe的CAS操作。 • 适用场景:高并发计数器(如统计请求次数)。


二、引用类型原子类

4. AtomicReference

特性:原子更新对象引用。 • 实现原理:通过volatile V valueUnsafecompareAndSwapObject实现引用替换。 • 示例

AtomicReference<String> ref = new AtomicReference<>("initial");
ref.compareAndSet("initial", "updated"); // 原子更新引用
5. AtomicStampedReference

特性:原子更新引用,并解决ABA问题(通过版本戳)。 • 实现原理:维护一个Pair对象,包含引用和int类型的版本戳。

public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp);
6. AtomicMarkableReference

特性:通过布尔标记解决ABA问题(无需版本号)。 • 实现原理:维护一个Pair对象,包含引用和boolean标记。

public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark);

三、数组类型原子类

7. AtomicIntegerArray

特性:原子更新整型数组中的元素。 • 实现原理:内部维护int[] array,通过UnsafegetAndAddInt操作原子更新指定索引的值。

public final int getAndAdd(int i, int delta);
8. AtomicLongArray

特性:原子更新长整型数组中的元素。 • 实现原理:与AtomicIntegerArray类似,针对long类型。

9. AtomicReferenceArray

特性:原子更新对象引用数组中的元素。 • 实现原理:通过UnsafecompareAndSwapObject实现数组元素的原子替换。


四、字段更新器

10. AtomicIntegerFieldUpdater

特性:原子更新对象的volatile int字段。 • 实现原理:通过反射获取字段的偏移量,使用Unsafe的CAS操作。 • 示例

public class Counter {
    private volatile int count;
}
AtomicIntegerFieldUpdater<Counter> updater = 
    AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
updater.getAndIncrement(counter); // 原子递增
11. AtomicLongFieldUpdater

特性:原子更新对象的volatile long字段。 • 实现原理:类似AtomicIntegerFieldUpdater,针对long类型。

12. AtomicReferenceFieldUpdater

特性:原子更新对象的volatile引用字段。 • 实现原理:通过反射和Unsafe的CAS操作替换引用。

AtomicReferenceFieldUpdater<User, String> updater = 
    AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");

五、高性能累加器

13. LongAdder / DoubleAdder

特性:高并发下比AtomicLong更高的吞吐量,适用于频繁更新但较少读取的场景。 • 实现原理:采用分段累加(Cell数组),减少线程竞争。

LongAdder adder = new LongAdder();
adder.add(1); // 分段累加
long sum = adder.sum(); // 合并所有分段的值
14. LongAccumulator / DoubleAccumulator

特性:支持自定义累加函数(如最大值、最小值)。 • 实现原理:类似LongAdder,但允许指定累加规则。

LongAccumulator maxAccumulator = new LongAccumulator(Long::max, 0);
maxAccumulator.accumulate(10); // 更新当前最大值

六、实现原理的核心技术

  1. CAS(Compare-And-Swap) • 通过Unsafe类提供的底层CAS方法(如compareAndSwapInt)实现无锁更新。 • 示例:AtomicInteger的自增操作:

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    
  2. Volatile变量 • 所有原子类的值均通过volatile修饰,保证可见性。 • 如AtomicInteger中的private volatile int value

  3. 分段累加(LongAdder) • 将单个变量拆分为多个Cell,线程竞争时分散到不同Cell。 • 最终结果通过合并所有Cell的值获得。

七、ABA 问题及解决方案

1. ABA 问题

  • 现象:变量从 A 改为 B 后又改回 A,CAS 无法感知中间变化。
  • 风险:可能导致数据一致性问题(如链表节点的中途修改)。

2. 解决方案

  • AtomicStampedReference:通过版本号(int stamp)标记状态。
  • AtomicMarkableReference:通过布尔标记(boolean mark)简化版本控制。

示例:

java

复制

AtomicStampedReference<String> stampedRef = 
    new AtomicStampedReference<>("A", 1);
​
int oldStamp = stampedRef.getStamp();
stampedRef.compareAndSet("A", "B", oldStamp, oldStamp + 1); // 更新值和版本号

八、适用场景对比

原子类适用场景
AtomicInteger/Long低竞争环境下的计数器(如简单统计)。
LongAdder高并发写入、低频读取(如QPS统计)。
AtomicReference原子更新对象引用(如单例模式的双重检查锁)。
AtomicStampedReference需解决ABA问题的场景(如无锁栈/队列)。
AtomicIntegerArray原子更新数组元素(如并行计算中的分片统计)。

九、总结

Java的原子类通过CAS和volatile变量实现无锁线程安全,适用于计数器、状态标志、对象引用更新等场景。针对不同需求,选择合适类型的原子类: • 基本类型AtomicIntegerAtomicLong。 • 引用类型AtomicReferenceAtomicStampedReference。 • 高并发累加LongAdderLongAccumulator。 • 字段更新AtomicIntegerFieldUpdater

理解其底层实现(如CAS、分段累加)有助于优化高并发程序的性能和可靠性。