如何在Java中实现线程安全的计数器_并发自增问题解决方案

Java线程

安全计数器首选AtomicInteger(基于CAS无锁高效),复杂逻辑用ReentrantLock,禁用volatile实现自增,高并发读多写少场景可选LongAdder。

在Java中实现线程安全的计数器,核心是避免多个线程同时读-改-写(read-modify-write)导致的竞态条件。直接用 intlong 配合 synchronized 虽可行,但不是最优解;现代Java更推荐使用原子类或显式锁来兼顾性能与安全性。

使用 AtomicInteger 实现高效自增

AtomicInteger 是最常用、最轻量的线程安全计数器方案。它基于CAS(Compare-and-Swap)机制,在硬件支持下实现无锁自增,性能远高于同步块。

  • 调用 incrementAndGet() 原子性完成“加1并返回新值”
  • 若需获取旧值再更新,可用 getAndIncrement()
  • 支持带条件的更新,如 compareAndSet(expected, updated)

示例:

  AtomicInteger counter = new AtomicInteger(0);
  counter.incrementAndGet(); // 线程安全,无需额外同步

需要复杂逻辑时:ReentrantLock + 普通变量

当计数逻辑不止简单自增(比如“仅当小于100才加1”,或需与其他共享状态联动),原子类的单一操作难以覆盖,此时应搭配可重入锁保证临界区原子性。

  • 声明 private final ReentrantLock lock = new ReentrantLock();
  • 所有读写操作前调用 lock.lock(),结束后在 finallyunlock()
  • synchronized 更灵活(支持尝试获取、超时、中断响应等)

避免常见陷阱:不要用 volatile 修饰普通整型

volatile 只能保证可见性和禁止重排序,**不保证复合操作的原子性**。以下代码仍是线程不安全的:

  volatile int count = 0;
  count++; // 实际分三步:读count → 加1 → 写回,中间可能被其他线程打断

除非只做单次写入或纯读取,否则 volatile 无法解决自增问题。

高并发场景下的进阶选择:LongAdder(JDK8+)

当计数器读多写少、且写操作极其频繁(如百万级TPS监控计数),AtomicInteger 的CAS失败重试开销会升高。LongAdder 通过分段累加(cell数组)降低竞争,最终调用 sum() 合并结果。

  • 写性能显著优于 AtomicLong,适合统计类场景
  • sum() 不是强一致的(可能略滞后),但对监控/采样足够准确
  • 初始化后直接用 increment(),语义与 AtomicInteger 一致