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] = 22.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 并发程序的性能,避免了传统锁机制带来的性能瓶颈。
