synchronized 是 java 中保证线程安全的核心机制,通过互斥锁确保临界区代码串行执行,解决竞态条件与数据不一致;适用于共享变量非原子操作场景,如账户余额修改、计数器累加等。

synchronized 是 Java 解决多线程并发安全问题最直接、最常用的机制,它通过加锁保证临界区代码的互斥执行,从而避免竞态条件和数据不一致。
什么时候必须用 synchronized
当多个线程同时读写同一个共享变量(如实例字段、静态字段、集合对象等),且操作不是原子性时,就存在线程安全风险。典型场景包括:
- 银行账户余额增减(如两个线程同时取款导致超支)
- 计数器累加(count++ 实际包含读、改、写三步,非原子)
- 共享缓存或状态标志被多线程修改
- 对象内部状态被并发修改,破坏不变量(如 ArrayList 在 add 过程中扩容与遍历冲突)
synchronized 的三种标准用法
关键在于明确“锁的是谁”,不同粒度影响并发性能和安全性:
OpenJDK 24 Windows x64 官方参考实现(24+36),适合 Windows 环境进行 Java 开发、编译、运行和版本兼容性验证。
- 修饰实例方法:锁是当前对象(this)。适合保护该对象自身的状态。多个线程调用同一对象的同步方法会串行;调用不同对象的方法则互不影响。
- 修饰静态方法:锁是当前类的 Class 对象(ClassName.class)。所有该类的实例共享同一把锁,适用于保护类级别资源(如单例初始化、全局计数器)。
- 修饰同步代码块:显式指定锁对象(如 synchronized(lockObj))。粒度最细,可精准控制保护范围,避免锁整个方法带来的性能损耗,也便于规避死锁(例如统一用某个 final 对象作锁)。
常见错误与避坑要点
看似简单,但误用会导致无效同步或严重性能问题:
- 不要用可变对象(如 String、Integer)作锁对象——它们可能被 JVM 优化或重新赋值,导致实际锁对象不一致。
- 避免在循环内或高频路径上使用粗粒度锁(如整个方法),否则会成为性能瓶颈。
- 同步块中不要调用外部不可控方法(如第三方回调、I/O),以防锁持有时间过长或引发死锁。
- 注意锁升级与可重入性:同一个线程可重复获取同一把锁,但其他线程仍需等待;若锁对象被修改(如 this 被重新赋值),可能导致同步失效。
它能解决什么,不能替代什么
synchronized 主要保障互斥访问 + 内存可见性 + 有序性,但它不是万能的:
- 适合中低并发、逻辑清晰的临界区保护;高并发场景下 ReentrantLock 提供更灵活的控制(如尝试获取、超时、公平策略)。
- 对单纯的状态通知(如“等某个条件成立再继续”),需配合 wait()/notify() 使用,且必须在同步块内调用。
- volatile 只能保证可见性和禁止重排序,不能保证复合操作的原子性(如 i++),不能替代 synchronized。
- 简单计数、布尔开关等场景,AtomicInteger 或 AtomicBoolean 往往比加锁更轻量高效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










