synchronized通过互斥锁保障线程安全,关键在于合理选择锁对象(实例锁、类锁或私有final对象)和最小化锁范围,同时兼顾可见性、原子性及规避字符串字面量锁、同步块内调用外部方法等陷阱。

synchronized 是 Java 中最直接、最常用的线程安全保障手段,它通过“互斥锁”机制确保共享资源在任意时刻只被一个线程访问,从而避免竞态条件、数据错乱等并发问题。关键不在于“加不加锁”,而在于“锁谁、锁哪、锁多大范围”。
明确锁对象:实例锁、类锁、自定义锁对象
锁的对象决定了同步的粒度和作用范围:
-
实例方法加 synchronized:锁的是当前实例(
this),多个线程调用不同对象的该方法互不影响;同一对象的多个 synchronized 实例方法之间互斥。 -
静态方法加 synchronized:锁的是当前类的 Class 对象(如
MyClass.class),所有线程对该类的静态同步方法都竞争同一把锁,适合保护静态变量或全局配置。 -
同步代码块 + 显式对象:最灵活的方式,例如
synchronized(lockObj) { ... }。推荐使用私有 final 对象(如private final Object lock = new Object();)作为锁,避免外部干扰,也防止因锁对象被修改导致同步失效。
避免锁范围过大:细粒度优先
锁的范围越小,线程阻塞越少,并发吞吐越高。不要把整个方法都包进 synchronized,尤其当方法中包含 I/O、网络调用或耗时计算时:
- 错误示例:在库存扣减方法里先查数据库、再校验、再更新,整个方法加 synchronized —— 所有请求排队,性能骤降。
- 正确做法:只对真正操作共享变量的核心逻辑加锁,比如“读取当前库存 → 判断是否充足 → 扣减并写回”这三步必须原子执行,其他非共享操作(如日志、响应封装)移出同步块。
- 若涉及多个变量需协同更新(如账户余额与流水记录),应确保它们被同一把锁保护,避免部分更新成功、部分失败导致状态不一致。
注意锁的可见性与原子性双重保障
synchronized 不仅提供互斥,还隐含了内存语义:
- 进入同步块前,线程会刷新本地工作内存,从主内存读取最新值(解决可见性);
- 退出同步块时,线程会将修改强制刷回主内存(保证其他线程可见);
- 整个同步块内执行的代码被视为一个不可分割的原子操作(解决复合操作的原子性,如
count++)。
避开常见陷阱
实际开发中容易忽略但影响严重的细节:
- 不要用字符串字面量、常量池对象(如
"lock")、或getClass()作锁对象——它们可能被其他代码意外共享,引发意外交互阻塞。 - 避免在同步块内调用外部可变对象的方法(如
synchronized(list) { list.sort(...); }),因为 list 的实现可能自己加锁,造成死锁风险。 - JDK 18 起已彻底移除偏向锁,无需再考虑其开关参数;轻量级锁和重量级锁的升级由 JVM 自动管理,开发者只需关注逻辑正确性,不必手动干预锁状态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











