原子操作类Atomic
原子操作类(Atomic
)是 Java 并发编程中的重要组成部分,用于在多线程环境中实现对基本类型的原子性操作。java.util.concurrent.atomic
包中的原子类提供了一些高效的无锁操作,通过硬件支持的原子操作来避免使用锁,从而提升并发性能。
# 1. 什么是原子操作类
- 原子性:原子性指的是一个操作不可中断,即使在多线程环境下,这个操作一旦开始执行,就不会被其他线程干扰。原子操作类提供了对共享变量的无锁原子操作,确保操作的原子性。
- CAS(Compare-And-Swap)机制:原子类内部依赖于硬件支持的 CAS 操作实现无锁更新。CAS 包含三个操作数——内存位置(要读取和更新的变量)、预期的旧值、要更新的新值。CAS 只有在变量的当前值等于预期值时,才会将其更新为新值,这样就保证了更新的原子性。
# 2. 常见的原子类
Java 中常用的原子类包括 AtomicInteger
、AtomicLong
、AtomicBoolean
以及 AtomicReference
,它们分别用于对基本类型和引用类型进行原子操作。
# 2.1 AtomicInteger
- 概念:
AtomicInteger
是一个支持原子操作的int
类型。它提供了一些线程安全的方法来更新整数值。 - 常用方法:
get()
:获取当前值。set(int newValue)
:设置为指定值。incrementAndGet()
:原子地自增并返回新值。compareAndSet(int expect, int update)
:如果当前值等于预期值,则原子地更新为新值。
示例代码:
AtomicInteger count = new AtomicInteger(0);
Runnable worker = () -> {
for (int i = 0; i < 5; i++) {
int newValue = count.incrementAndGet();
System.out.println(Thread.currentThread().getName() + " incremented count to " + newValue);
}
};
for (int i = 0; i < 3; i++) {
new Thread(worker).start();
}
在这个例子中,多个线程同时对 count
进行自增操作,AtomicInteger
确保了操作的线程安全。
# 2.2 AtomicLong
- 概念:
AtomicLong
类与AtomicInteger
类类似,只不过它用于long
类型的原子操作。 - 常用方法:
getAndAdd(long delta)
:原子地将当前值增加指定的delta
,并返回旧值。addAndGet(long delta)
:原子地将当前值增加指定的delta
,并返回新值。
# 2.3 AtomicBoolean
- 概念:
AtomicBoolean
提供了对布尔值的原子操作,适用于需要线程安全地修改标志位的场景。 - 常用方法:
compareAndSet(boolean expect, boolean update)
:如果当前值等于预期值,则原子地将其更新为新值。getAndSet(boolean newValue)
:获取当前值并将其设置为新值。
示例代码:
AtomicBoolean flag = new AtomicBoolean(false);
Runnable task = () -> {
if (flag.compareAndSet(false, true)) {
System.out.println(Thread.currentThread().getName() + " set the flag to true");
} else {
System.out.println(Thread.currentThread().getName() + " could not set the flag");
}
};
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
在这个例子中,只有一个线程能够成功将 flag
设置为 true
,其余线程将会失败。
# 2.4 AtomicReference
- 概念:
AtomicReference
提供了对引用类型变量的原子操作,适用于需要原子性更新引用对象的场景。 - 常用方法:
get()
:获取当前引用。compareAndSet(V expect, V update)
:如果当前引用等于预期引用,则原子地将其更新为新的引用。
示例代码:
AtomicReference<String> reference = new AtomicReference<>("Initial");
Runnable updater = () -> {
String oldValue = reference.get();
String newValue = oldValue + " -> Updated by " + Thread.currentThread().getName();
if (reference.compareAndSet(oldValue, newValue)) {
System.out.println(Thread.currentThread().getName() + " successfully updated the reference");
}
};
for (int i = 0; i < 3; i++) {
new Thread(updater).start();
}
在这个例子中,AtomicReference
确保多个线程可以安全地更新引用对象。
# 3. 使用场景
- 计数器:
AtomicInteger
和AtomicLong
非常适合用作计数器,例如请求数、连接数等。 - 状态标志:
AtomicBoolean
适合用于控制任务是否被执行,例如单次任务执行标志位。 - CAS 实现乐观锁:原子类通过 CAS 操作实现无锁更新,适用于高并发环境下对资源的乐观锁控制。
# 4. 原子类与锁的比较
- 性能:原子类使用 CAS 操作实现无锁更新,在大多数情况下性能优于使用锁的方式,尤其是在竞争不激烈的环境下。
- 复杂性:原子类适用于简单的变量更新,如果涉及多个变量的复杂更新逻辑,使用锁(如
synchronized
或ReentrantLock
)可能更加直观和方便。
# 5. 总结
原子操作类是 Java 并发包中的重要组成部分,通过无锁的方式实现了对共享变量的原子更新,保证了多线程环境下的线程安全。理解 AtomicInteger
、AtomicLong
、AtomicBoolean
以及 AtomicReference
的使用场景,有助于在高并发编程中编写更加高效和可靠的代码。在使用原子类时,如果操作复杂或涉及多个变量的更新,建议考虑使用锁来确保线程安全。
上次更新: 2024/11/01, 13:45:14