synchronized(this) 锁实例对象,保护实例字段;synchronized(ClassName.class) 锁类对象,保护静态字段;二者锁池独立、互不干扰,混用需谨慎。

为什么 synchronized(this) 和 synchronized(ClassName.class) 锁的不是一回事
对象锁和类锁根本不在同一个锁池里,它们互不干扰。用 this 锁实例方法,只拦住同一对象的多个线程;用 ClassName.class 锁静态方法或代码块,拦住的是整个类的所有实例——哪怕你 new 出十个对象,它们调静态同步块时也得排队。
常见错误现象:
– 多个线程操作不同对象的 synchronized(this) 方法,数据仍被并发修改
– 误以为给一个工具类的静态方法加了 synchronized 就能保护所有实例字段,结果字段是实例的,锁却是类的,完全没覆盖到
- 实例字段(如
private int count)必须用对象锁(synchronized(this)或同步实例方法)保护 - 静态字段(如
private static int totalCount)必须用类锁(synchronized(Counter.class)或同步静态方法)保护 - 混用场景(如单例 + 静态计数器)要特别注意:单例对象的锁 ≠ 类锁,别想当然认为“只有一个实例”就等价于“全局唯一锁”
synchronized 方法 vs synchronized 代码块,哪个更可控
方法级同步锁粒度太粗,容易拖慢无关逻辑。比如一个耗时 IO 操作和一个简单计数写在同一个同步方法里,后者就得傻等前者完成。
使用场景:
– 方法体短、逻辑纯(如 getter/setter)、且明确只操作本对象字段 → 可直接用 synchronized 方法
– 涉及外部调用、条件判断、或只有一小段需要保护 → 必须用同步代码块,精准锁住临界区
- 同步代码块必须显式指定锁对象,不能只写
synchronized{...}(语法错误) - 锁对象要是稳定引用:用
this、private final Object lock = new Object()都行;但别用new Object()临时创建,每次都不一样,等于没锁 - 避免锁
String、Integer等可能被常量池复用的对象,会导致意外的锁竞争
锁升级失败?检查你是不是在锁对象上做了 wait()/notify()
wait() 和 notify() 必须在已获取该对象锁的同步块内调用,否则抛 IllegalMonitorStateException。这不是“锁没生效”,而是 JVM 强制要求:只有持锁线程才能让出锁并等待,或唤醒其他等待者。
常见错误现象:
– 在非同步方法里直接调 obj.wait() → 立刻崩溃
– 同步块里锁的是 this,却对另一个对象 otherObj.notify() → 报错
-
wait()会释放当前持有的锁,并进入等待队列;notify()不释放锁,只是发信号 - 用
wait(long timeout)替代无参wait(),防止无限期挂起 - 永远在
while循环里检查条件(而不是if),因为虚假唤醒真实存在
高并发下 synchronized 的性能和替代方案边界在哪
从 JDK 6 开始,synchronized 已经有偏向锁、轻量级锁、重量级锁的自动升级机制,低竞争时开销极小。但一旦发生锁竞争(尤其是多核 CPU 上频繁争抢),还是会有明显上下文切换和阻塞延迟。
性能影响:
– 单线程/低并发:基本无感
– 中高并发(百级以上线程争同一把锁):吞吐下降明显,监控能看到 java.lang.Thread.State: BLOCKED 线程堆积
- 优先考虑缩小同步范围:用局部变量、ThreadLocal、CAS(
AtomicInteger)代替锁 - 读多写少场景,用
ReentrantReadWriteLock比单纯synchronized更高效 - 别为了“看起来高级”而提前上
StampedLock或LongAdder,先确认synchronized真成了瓶颈(用 JFR 或 Arthas 观察)
真正容易被忽略的是锁的语义一致性:比如两个方法分别保护不同字段,但业务上它们必须原子执行——这时候光靠两把独立的锁没用,得合并成一把锁,或者改用更高级的协调机制。锁本身不难写,难的是准确刻画“什么必须一起发生”。

















