synchronized、ReentrantLock、AtomicInteger 和 volatile 各有适用场景:synchronized 保证临界区串行执行;ReentrantLock 支持可中断和超时;AtomicInteger 适用于单变量原子操作;volatile 仅保证可见性,不保证原子性。

用 synchronized 锁住临界区是最直接的方式
当多个线程可能同时读写同一个共享变量(比如一个 int counter),不加控制就会出现“写丢失”——两个线程都读到 5,各自加 1 后都写回 6,结果本该是 7。用 synchronized 方法或代码块能强制串行执行:
常见写法有两种:
- 实例方法上加 synchronized:锁的是当前对象(this)
- 静态方法上加 synchronized:锁的是类的 Class 对象
- 更灵活的是用 synchronized(obj) { ... },显式指定锁对象,避免锁太粗影响并发度
注意点:
- 锁对象不能是基本类型包装类(如 Integer)、字符串字面量或可变对象(如 ArrayList),否则容易因对象替换或字符串常量池导致锁失效
- 不要在同步块里调用外部可变对象的未知方法,防止死锁或阻塞延长
ReentrantLock 比 synchronized 多了可中断和超时能力
当需要“尝试获取锁但不想无限等待”,或者要响应线程中断时,ReentrantLock 是更合适的选择。它支持 tryLock()、tryLock(long, TimeUnit) 和 lockInterruptibly()。
使用要点:
- 必须在 finally 块中调用 unlock(),否则可能永久持有锁
- 不要混用 synchronized 和 ReentrantLock 保护同一资源,它们互不感知
- 如果只是简单计数,优先考虑 AtomicInteger 而不是锁,性能更高且无锁开销
用 AtomicInteger 替代锁做简单状态更新最轻量
对单个整型变量的自增、自减、比较并设置(CAS)等操作,AtomicInteger 是零锁方案。它底层依赖 CPU 的 CAS 指令,比锁快得多,也不存在死锁风险。
典型场景:
- 计数器(如请求统计、连接数)
- 状态标志(如 AtomicBoolean running = new AtomicBoolean(true))
- 序列号生成(配合 incrementAndGet())
限制:
- 只适用于单变量原子操作;多个变量联动更新(如“扣库存同时改订单状态”)不能靠多个原子类拼凑,仍需锁或事务
- getAndIncrement() 和 incrementAndGet() 返回值不同,别用反了
volatile 关键字只保可见性,不保原子性
volatile 能让线程立刻看到其他线程对该变量的最新写入,但它**不能防止指令重排序带来的逻辑错误,也不能保证复合操作的原子性**。比如 flag = true 可以用 volatile,但 counter++ 不行——因为这是“读-改-写”三步,volatile 不拦中间那两步。
立即学习“Java免费学习笔记(深入)”;
适用场合:
- 纯状态标记(如 volatile boolean shutdownRequested)
- 作为双重检查锁(DCL)中单例实例的修饰符(配合 synchronized)
- 与 AtomicReference 配合实现无锁数据结构
常见误用:
- 把 volatile List 当线程安全集合用(add() 仍会出并发问题)
- 以为加了 volatile 就不用 synchronized 了,结果发现数值还是错的
实际项目里,多数一致性问题不是“选哪个机制”,而是“哪段逻辑真正需要保护”。先厘清共享数据的读写边界,再决定用 volatile、原子类、锁,还是干脆重构为无共享设计。锁粒度太粗吞吐低,太细又容易漏;原子类看着简洁,但组合操作一多就容易掉进非原子陷阱。








