侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计一个线程安全的计数器,支持高并发自增和获取当前值

2025-12-5 / 0 评论 / 4 阅读

题目

设计一个线程安全的计数器,支持高并发自增和获取当前值

信息

  • 类型:问答
  • 难度:⭐⭐

考点

线程安全,原子操作,并发容器,性能优化

快速回答

实现线程安全计数器的核心方案:

  • 使用AtomicLongLongAdder(JDK8+)实现原子操作
  • 避免使用synchronizedvolatile+锁的原始方案
  • 高并发场景优先选择LongAdder减少竞争
  • 获取最终值时注意调用sum()方法合并单元格
## 解析

问题背景

在高并发场景下设计计数器需解决两个核心问题:1)多线程自增操作的原子性;2)保证获取当前值的可见性。常见错误方案如使用volatile longsynchronized方法会导致性能瓶颈或数据不一致。

解决方案对比

方案1:AtomicLong(基础版)

import java.util.concurrent.atomic.AtomicLong;

public class AtomicCounter {
    private final AtomicLong count = new AtomicLong(0);

    public void increment() {
        count.incrementAndGet();
    }

    public long getCount() {
        return count.get();
    }
}

原理说明:基于CAS(Compare-And-Swap)实现无锁更新,适合低竞争场景。但在高并发下大量线程重试会导致CPU资源浪费。

方案2:LongAdder(JDK8+ 优化版)

import java.util.concurrent.atomic.LongAdder;

public class AdderCounter {
    private final LongAdder count = new LongAdder();

    public void increment() {
        count.increment();
    }

    public long getCount() {
        return count.sum();
    }
}

原理说明:采用分段累加(Cell数组)分散竞争,线程优先更新自己的Cell,最后合并结果。吞吐量比AtomicLong高5倍以上(基准测试数据)。

最佳实践

  • 首选LongAdder:适用于写多读少的高并发场景(如统计点击数)
  • 精确场景用AtomicLong:需要实时精确值且竞争不激烈时使用
  • 避免误区
    • 错误1:volatile long + synchronized ➜ 锁竞争导致性能骤降
    • 错误2:直接调用LongAdder.longValue() ➜ 实际应调用sum()

性能测试数据(参考)

线程数AtomicLong (ops/ms)LongAdder (ops/ms)
115,0008,000
102,50045,000
10030052,000

扩展知识

  • CAS的ABA问题:AtomicLong可能遇到,但计数器场景不影响正确性
  • 最终一致性:LongAdder的sum()结果非实时精确值,但保证最终正确
  • 分布式计数器:超大规模系统可考虑Redis原子操作或分片统计