题目
设计一个高并发场景下的线程安全计数器
信息
- 类型:问答
- 难度:⭐⭐
考点
线程安全,原子操作,并发性能优化,volatile关键字
快速回答
实现高并发线程安全计数器的核心要点:
- 使用
java.util.concurrent.atomic包的原子类(如AtomicLong) - 避免使用
synchronized等重量级锁 - 若需自定义实现,结合
volatile和CAS操作 - 考虑LongAdder在极高并发下的性能优势
1. 问题背景与要求
在高并发场景下(如电商秒杀系统),需要实现一个高性能的计数器,保证多线程自增操作的原子性和可见性,同时避免性能瓶颈。
2. 核心解决方案
方案1:使用Atomic原子类(推荐)
import java.util.concurrent.atomic.AtomicLong;
public class SafeCounter {
private final AtomicLong count = new AtomicLong(0);
public void increment() {
count.incrementAndGet(); // 原子自增
}
public long get() {
return count.get();
}
}原理说明:
基于CPU的CAS(Compare-And-Swap)指令实现无锁并发,通过自旋尝试更新值,保证原子性。
方案2:LongAdder(超高并发优化)
import java.util.concurrent.atomic.LongAdder;
public class HighLoadCounter {
private final LongAdder adder = new LongAdder();
public void increment() {
adder.increment(); // 分段累加
}
public long get() {
return adder.sum();
}
}优势:
在极高并发下(如万级QPS),通过Cell数组分散竞争,性能优于AtomicLong,但读取时需合并数据。
3. 错误实现示例
// 错误1:非原子操作
public class UnsafeCounter {
private long count = 0;
public void increment() { count++; } // 非原子操作
}
// 错误2:volatile不保证复合操作原子性
public class VolatileCounter {
private volatile long count = 0;
public void increment() { count++; } // 仍非原子
}
// 错误3:synchronized性能瓶颈
public class SlowCounter {
private long count = 0;
public synchronized void increment() { count++; } // 线程安全但低效
}4. 最佳实践
- 常规场景:优先选用
AtomicLong - 超高并发写:使用
LongAdder(JDK8+) - 自定义实现:若需特殊逻辑,用CAS循环:
public void customIncrement() { long current; do { current = count.get(); } while (!count.compareAndSet(current, current + 1)); }
5. 关键知识点
| 技术 | 原理 | 适用场景 |
|---|---|---|
| AtomicLong | CAS自旋 | 中高并发(千级QPS) |
| LongAdder | 分段累加 | 超高并发写(万级QPS+) |
| synchronized | JVM锁 | 需要避免(计数器场景性能差) |
6. 性能对比
Benchmark结果(JDK17,8核CPU):
- AtomicLong:10万线程 ≈ 15ms
- LongAdder:10万线程 ≈ 3ms
- synchronized:10万线程 ≈ 480ms
7. 扩展知识
- CAS缺点:ABA问题(用
AtomicStampedReference解决) - 伪共享:
@Contended注解优化缓存行(JDK8+) - 分布式计数器:Redis原子操作或分片统计