Atomic 原子类总结
Java 的 java.util.concurrent.atomic
包提供了一系列原子操作类,称为 Atomic 类,用于实现线程安全的基本类型操作。这些类依赖于底层的原子性操作(如 CAS
操作),能够在多线程环境中安全地执行变量更新,避免了使用传统的 synchronized
机制所带来的性能开销。
Atomic 类的关键优势是高效并发控制,它们允许在不使用锁的情况下,保证多个线程对共享变量的更新操作具有原子性。
1. Atomic 类概述
1.1 原子性(Atomicity)
原子性是指操作在执行时不可中断,执行过程中其他线程无法看到操作的中间状态。在 Java 中,原子类通常基于 CAS(Compare-And-Swap)机制,它能确保操作是 线程安全的,且在多线程环境中无锁地进行。
CAS 操作是 CPU 提供的原子操作,可以在单个硬件指令周期内比较和交换数据。原子类通过 CAS 来实现线程安全的更新。
1.2 不同的 Atomic 类
Java 提供了多种 Atomic 类来支持不同类型的变量更新,常见的有以下几种:
AtomicInteger
:对int
类型的原子更新。AtomicLong
:对long
类型的原子更新。AtomicBoolean
:对boolean
类型的原子更新。AtomicReference
:对对象引用的原子更新。AtomicIntegerArray
、AtomicLongArray
:对数组中元素的原子更新。AtomicMarkableReference
和AtomicStampedReference
:分别用于带标记和带时间戳的原子引用更新。
2. 常见的 Atomic 类及其方法
2.1 AtomicInteger
AtomicInteger
是对 int
类型变量进行原子更新的类。常用方法包括:
get()
:获取当前值。set(int newValue)
:设置值。getAndSet(int newValue)
:获取当前值,并设置新值。incrementAndGet()
:将当前值加 1,并返回新值。decrementAndGet()
:将当前值减 1,并返回新值。addAndGet(int delta)
:将当前值加上给定的增量,并返回新值。compareAndSet(int expect, int update)
:如果当前值等于期望值,则将其更新为新值;返回是否更新成功。
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet(); // 1
atomicInt.addAndGet(5); // 6
atomicInt.compareAndSet(6, 10); // true, 更新成功
2.2 AtomicLong
AtomicLong
类类似于 AtomicInteger
,但是它操作的是 long
类型的变量。它也具有类似的方法,如:
get()
set(long newValue)
getAndSet(long newValue)
incrementAndGet()
decrementAndGet()
addAndGet(long delta)
compareAndSet(long expect, long update)
AtomicLong atomicLong = new AtomicLong(1000);
atomicLong.addAndGet(500); // 1500
atomicLong.compareAndSet(1500, 2000); // true, 更新成功
2.3 AtomicBoolean
AtomicBoolean
类用于对 boolean
值进行原子操作。常用方法如下:
get()
:获取当前值。set(boolean newValue)
:设置新值。getAndSet(boolean newValue)
:获取当前值并设置新值。compareAndSet(boolean expect, boolean update)
:如果当前值等于期望值,则将其更新为新值。
AtomicBoolean atomicBoolean = new AtomicBoolean(true);
atomicBoolean.set(false); // 设置为 false
atomicBoolean.compareAndSet(false, true); // true, 更新成功
2.4 AtomicReference
AtomicReference
用于原子地操作对象引用。常用方法如下:
get()
:获取当前引用。set(V newValue)
:设置新引用。getAndSet(V newValue)
:获取当前引用并设置新引用。compareAndSet(V expect, V update)
:如果当前引用等于期望引用,则将其更新为新引用。
AtomicReference<String> atomicRef = new AtomicReference<>("Hello");
atomicRef.compareAndSet("Hello", "World"); // true, 更新成功
atomicRef.get(); // "World"
2.5 AtomicIntegerArray
和 AtomicLongArray
这些类分别用于原子地操作 int[]
和 long[]
数组中的元素。它们的方法类似于 AtomicInteger
和 AtomicLong
,但是操作的是数组中的指定元素。
AtomicIntegerArray atomicArray = new AtomicIntegerArray(new int[] {1, 2, 3});
atomicArray.incrementAndGet(0); // atomicArray[0] = 2
2.6 AtomicMarkableReference
和 AtomicStampedReference
这两个类分别用于带有标记和带有时间戳的引用更新,适用于解决 ABA 问题。
AtomicMarkableReference
:带有布尔标记,通常用于指示对象的状态。AtomicStampedReference
:带有时间戳,通常用于版本控制。
3. Atomic 类的应用场景
3.1 计数器
原子类经常用于实现线程安全的计数器,避免了使用 synchronized
或 ReentrantLock
造成的性能瓶颈。例如,在高并发的场景中,如果需要对某个计数器进行增、减、查询操作,可以使用 AtomicInteger
。
3.2 高效的并发控制
在多线程程序中,多个线程可能会同时更新同一个共享变量,使用 Atomic
类可以保证每次更新操作都是原子性的,避免了使用锁来保证同步,从而提高了性能。
3.3 用于替代锁的同步器
原子类可以用来替代传统的锁(如 synchronized
或 ReentrantLock
),尤其在那些对简单共享变量进行原子更新的场景中,原子类提供了更高效的替代方案。
例如,使用 AtomicBoolean
来实现一个简单的状态标志,可以避免使用显式的锁。
3.4 在非阻塞算法中使用
原子类在实现非阻塞算法(如无锁队列、栈等)时非常有用,因为它提供了对共享变量的原子操作,可以在多线程环境下高效执行操作。
4. Atomic 类的优缺点
4.1 优点
- 高效性:原子类通过底层的 CAS 操作避免了传统锁(如
synchronized
或ReentrantLock
)的开销,在高并发的场景下表现更好。 - 简洁性:原子类提供了简单的 API,开发者可以轻松实现线程安全的共享变量更新。
- 无锁机制:相比于传统的锁机制,原子类通过 CAS 操作实现线程安全,避免了锁的竞争和线程阻塞。
4.2 缺点
- ABA 问题:CAS 操作可能会遇到 ABA 问题,即一个值可能被修改了多次,但最终的值还是原来的值,这在某些应用中可能导致错误。
AtomicStampedReference
和AtomicMarkableReference
可以帮助解决这个问题。 - 有限的功能:原子类仅提供一些简单的原子操作,不能处理复杂的同步场景。在复杂的并发控制需求下,可能还是需要使用更高级的同步机制(如
ReentrantLock
)。
总结
Atomic
类提供了高效、无锁的线程安全变量操作。- 这些类主要基于 CAS 操作,在简单场景下能够提高性能。
- 常见的
Atomic
类包括:AtomicInteger
、AtomicLong
、AtomicBoolean
、AtomicReference
等。 - 适用于高并发场景中的计数器、状态标志等场景,但对于复杂的同步场景,可能需要使用其他同步机制。
通过合理使用 Atomic
类,可以有效地提升 Java 并发程序的性能,避免了传统锁机制带来的性能瓶颈。