synchronized修饰实例方法时锁的是this对象,若多线程操作不同实例则无互斥效果;保护静态共享状态需用类锁或静态同步方法,推荐私有final对象作为锁以避免外部干扰和死锁。

为什么直接在方法上加 synchronized 有时不生效
因为 synchronized 修饰实例方法时,锁的是当前对象(this),不是类。如果多个线程操作的是不同实例,锁就互不干扰——看似加了锁,实际没串行。
- 场景:创建了
new Counter()两次,分别由两个线程调用其increment()方法 → 无锁竞争 - 解决:想保护类级别的共享状态(如静态计数器),必须用
synchronized(Counter.class)或修饰静态方法 - 注意:
synchronized静态方法锁的是Class对象,和任何实例锁都不冲突,也不共享
synchronized(this) 和 synchronized(任意对象) 的区别
本质都是对“某个对象实例”加 monitor 锁,但选择哪个对象决定锁的粒度和安全性。
-
synchronized(this):锁当前实例,简单但危险——外部代码可能拿到this并也去同步它,导致死锁或意外阻塞 -
synchronized(lockObj):推荐用私有 final 对象(如private final Object lock = new Object();),对外不可见,避免锁被误用 - 别用字符串、常量或
getClass()做锁对象——容易因字符串驻留或继承关系引发意外锁共享
同步代码块比同步方法更灵活,但要注意锁对象一致性
同步方法是语法糖,底层都转成 monitorenter/monitorexit 指令;同步代码块能精确控制临界区范围,减少锁持有时间。
public void transfer(Account from, Account to, BigDecimal amount) {
// 错误:按任意顺序锁,可能死锁
synchronized(from) {
synchronized(to) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
public void transferSafe(Account from, Account to, BigDecimal amount) {
// 正确:约定按 hashCode 顺序加锁,避免死锁
Object firstLock = from.hashCode() < to.hashCode() ? from : to;
Object secondLock = from.hashCode() < to.hashCode() ? to : from;
synchronized(firstLock) {
synchronized(secondLock) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
- 同步方法无法拆分临界区,哪怕只有几行需要保护,整个方法都会被阻塞
- 多个同步块若使用不同锁对象,彼此不互斥——必须确保所有访问同一共享资源的路径,都用同一个锁对象
- 锁对象一旦确定,就不能为 null,否则抛
NullPointerException
同步锁不能继承、不能中断、也不能超时
synchronized 是 JVM 原生实现的独占锁,没有高级特性。遇到需要响应中断、设定超时、或尝试非阻塞获取的场景,就得换 java.util.concurrent.locks.ReentrantLock。
立即学习“Java免费学习笔记(深入)”;
- 无法中断正在等待
synchronized锁的线程(Thread.interrupt()无效) - 没有
tryLock(long timeout, TimeUnit unit)这类机制,容易无限等待 - 不支持条件队列的多路等待(
ReentrantLock.newCondition()可以配多个Condition) - 但优点是简洁、不易出错、JVM 优化成熟——简单场景优先用
synchronized,复杂需求再切到Lock
synchronized 很快,但判断“这里该锁谁、锁多久、有没有被其他地方悄悄共用了同一个锁”,才决定它到底起不起作用。









