一、为什么需要Atomic?
在多线程环境中,简单的i++操作并非原子性,这会导致竞态条件问题。传统解决方案是使用synchronized关键字,但 synchronized 是一种悲观锁,存在性能开销大、容易死锁等问题。
Java从JDK 1.5开始提供了java.util.concurrent.atomic包,通过CAS(Compare-And-Swap)操作实现了无锁算法,无需加锁就能保证线程安全,大幅提升了并发性能。
二、Atomic类核心实现原理
Atomic类的核心实现基于CPU硬件提供的CAS指令。CAS操作包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。当且仅当V的值等于A时,才会将V的值更新为B,否则不执行任何操作。
// CAS操作伪代码
public boolean compareAndSwap(int value, int expect, int update) {
if(value == expect) {
value = update;
return true;
}
return false;
}
三、AtomicInteger实战示例
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static AtomicInteger atomicCount = new AtomicInteger(0);
private static int normalCount = 0;
public static void main(String[] args) throws InterruptedException {
// 创建100个线程并发计数
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
atomicCount.incrementAndGet(); // 原子操作
normalCount++; // 非原子操作
}
});
threads[i].start();
}
// 等待所有线程执行完毕
for (Thread thread : threads) {
thread.join();
}
System.out.println("Atomic count: " + atomicCount.get());
System.out.println("Normal count: " + normalCount);
}
}
运行结果:
Atomic count: 100000
Normal count: 98632 // 每次运行结果不一致,小于100000
从结果可以看出,AtomicInteger保证了操作的原子性,而普通变量无法保证。
四、Atomic类常用方法详解
- 基本操作
AtomicInteger atomicInt = new AtomicInteger(0);
// 原子性增加
int currentValue = atomicInt.incrementAndGet(); // ++i
int currentValue = atomicInt.getAndIncrement(); // i++
// 原子性更新
atomicInt.addAndGet(10); // 增加指定值
atomicInt.getAndAdd(10);
// CAS操作
boolean success = atomicInt.compareAndSet(expect, update);
- 复杂原子操作
// 使用Lambda表达式更新值
atomicInt.updateAndGet(x -> x * 2);
atomicInt.getAndUpdate(x -> x * 2);
// 累积操作
atomicInt.accumulateAndGet(10, (x, y) -> x + y);
五、其他Atomic类应用
- AtomicReference:原子更新对象引用
AtomicReference<User> atomicUser = new AtomicReference<>();
User oldUser = new User("John");
User newUser = new User("Alice");
atomicUser.set(oldUser);
atomicUser.compareAndSet(oldUser, newUser);
- AtomicLongArray:原子更新long型数组
AtomicLongArray atomicArray = new AtomicLongArray(10);
atomicArray.set(0, 100L);
atomicArray.getAndAdd(0, 20L);
六、Atomic类的适用场景与局限性
适用场景:
- 计数器、累加器等简单原子操作
- 频繁读写的并发场景
- 需要避免死锁的高并发环境
局限性:
- ABA问题(可通过AtomicStampedReference解决)
- 复杂复合操作仍需使用锁
- 高竞争环境下CAS失败率较高
七、性能对比测试
通过基准测试对比synchronized与Atomic的性能差异:
// 使用JMH进行性能测试
@BenchmarkMode(Mode.Throughput)
public class PerformanceTest {
private int syncCount = 0;
private AtomicInteger atomicCount = new AtomicInteger(0);
@Benchmark
public void synchronizedIncrement() {
synchronized(this) {
syncCount++;
}
}
@Benchmark
public void atomicIncrement() {
atomicCount.incrementAndGet();
}
}
测试结果通常显示Atomic操作比synchronized快2-3倍,但在高竞争环境下优势可能减小。
总结
Atomic类通过无锁算法提供了高性能的线程安全解决方案,特别适用于读多写少或简单原子操作的并发场景。但它并非万能钥匙,对于复杂的复合操作,仍需要结合synchronized或Lock使用。合理选择线程同步机制是编写高效并发程序的关键。
选择合适的工具应对不同的并发场景,才能充分发挥Java并发编程的强大能力。
9万+

被折叠的 条评论
为什么被折叠?



