JUC 包中的 Atomic 原子类总结

229 阅读3分钟

JUC 包中的 Atomic 原子类总结

1. Atomic原子类概念

  • 定义:Atomic类提供线程安全的方式来操作单个变量,操作具有原子性,即不可分割、不可中断。
  • CAS机制:Atomic类依赖CAS(Compare-And-Swap,比较并交换)乐观锁保证其方法的原子性,避免使用传统锁机制。

2. JUC原子类分类

  • 基本类型:包括AtomicIntegerAtomicLongAtomicBoolean,用于原子地更新基本数据类型。
  • 数组类型:包括AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray,用于原子地更新数组中的元素。
  • 引用类型:包括AtomicReferenceAtomicStampedReferenceAtomicMarkableReference,用于原子地更新对象引用,其中AtomicStampedReferenceAtomicMarkableReference可以解决ABA问题。
  • 对象属性修改类型:包括AtomicIntegerFieldUpdaterAtomicLongFieldUpdaterAtomicReferenceFieldUpdater,用于原子地更新某个类的指定字段。

3. 基本类型Atomic类示例(以AtomicInteger为例)

方法介绍

  • get():获取当前值。
  • getAndSet(int newValue):获取当前值并设置新值。
  • getAndIncrement():获取当前值并自增。
  • getAndDecrement():获取当前值并自减。
  • getAndAdd(int delta):获取当前值并加上预期值。
  • compareAndSet(int expect, int update):如果当前值等于预期值,则以原子方式更新。
  • lazySet(int newValue):最终设置为新值,但可能更高效,允许其他线程在一段时间内读取旧值。

示例代码

AtomicInteger atomicInt = new AtomicInteger(0);
int tempValue = atomicInt.getAndSet(3);
tempValue = atomicInt.getAndIncrement();
tempValue = atomicInt.getAndAdd(5);
boolean updateSuccess = atomicInt.compareAndSet(9, 10);
atomicInt.lazySet(15);

4. 数组类型Atomic类示例(以AtomicIntegerArray为例)

方法介绍

  • get(int i):获取数组索引i处的元素值。
  • getAndSet(int i, int newValue):获取索引i处的值并设置新值。
  • getAndIncrement(int i):获取索引i处的值并自增。
  • compareAndSet(int i, int expect, int update):如果索引i处的值等于预期值,则以原子方式更新。

示例代码

int[] nums = {1, 2, 3, 4, 5, 6};
AtomicIntegerArray atomicArray = new AtomicIntegerArray(nums);
int tempValue = atomicArray.getAndSet(0, 2);
tempValue = atomicArray.getAndIncrement(0);
tempValue = atomicArray.getAndAdd(0, 5);

5. 引用类型Atomic类示例(以AtomicReference为例)

方法介绍

与基本类型类似,提供get()set()compareAndSet()等方法,用于操作对象引用。

示例代码

AtomicReference<Person> ar = new AtomicReference<>(new Person("SnailClimb", 22));
Person updatePerson = new Person("Daisy", 20);
ar.compareAndSet(ar.get(), updatePerson);

6. 带版本号和标记的引用类型Atomic类示例

  • AtomicStampedReference示例:解决ABA问题,通过版本号控制更新。
  • AtomicMarkableReference示例:类似AtomicStampedReference,但使用布尔标记代替整数值。

7. 对象属性修改类型Atomic类示例(以AtomicIntegerFieldUpdater为例)

步骤

  1. 首先使用newUpdater()创建更新器。
  2. 然后调用更新器的方法(如incrementAndGet())来原子地更新对象属性。

示例代码

AtomicIntegerFieldUpdater<Person> ageUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");
Person person = new Person("SnailClimb", 22);
ageUpdater.incrementAndGet(person);
ageUpdater.compareAndSet(person, 28, 30);

8. 注意事项

  • public volatile修饰符:使用AtomicIntegerFieldUpdaterAtomicLongFieldUpdaterAtomicReferenceFieldUpdater时,被更新的字段必须是public volatile的,以确保可见性和禁止指令重排序。
  • ABA问题:在某些情况下,使用简单的CAS机制可能会遇到ABA问题。ABA问题指的是一个变量被另一个线程读取后,又被该线程修改回原值,但期间可能被其他线程更改过。AtomicStampedReferenceAtomicMarkableReference通过引入版本号或布尔标记来解决这个问题。
  • lazySet方法lazySet提供了一种比set方法更弱的内存顺序保证,它可能会使得其他线程在之后的一小段时间内还能读取到旧值,但在某些情况下可能会更高效。

9. 结论

java.util.concurrent.atomic包中的Atomic原子类提供了一种高效的线程安全方式来操作单个变量或对象的字段,避免了使用重量级的锁机制。通过CAS乐观锁,这些类能够在多线程环境中保持操作的原子性。不同的Atomic类适用于不同的数据类型和操作场景,包括基本类型、数组、引用类型以及对象的字段更新。正确理解和使用这些类,对于开发高性能的并发应用至关重要。