synchronized实例方法锁的是this对象,因为jvm隐式以当前实例为监视器,同实例的同步方法互斥,不同实例则并发执行。

为什么 synchronized 实例方法锁的是 this 对象
因为 synchronized 修饰实例方法时,JVM 会隐式以当前对象(即 this)作为锁对象。调用该方法前,线程必须先获取该实例的监视器(monitor);其他线程若想调用同一对象的任意 synchronized 实例方法,会被阻塞,直到锁释放。
注意:不同实例之间互不影响——obj1.method() 和 obj2.method() 可并发执行,因为它们锁的是各自的 this。
怎么写一个典型的 synchronized 实例方法
直接在方法声明前加 synchronized 关键字即可,无需显式指定锁对象:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
等价于手动用 this 做同步块:
public void increment() {
synchronized (this) {
count++;
}
}
- 两种写法语义完全一致,编译后字节码中都使用
monitorenter/monitorexit - 推荐用
synchronized方法语法,更简洁;但若只希望部分代码同步,或需锁其他对象,就该用同步块 - 不能继承同步性:子类重写该方法时,
synchronized不会自动带上,必须显式声明
常见误用和容易踩的坑
看似加了锁,实际没效果的情况很常见:
- 把
synchronized加在static方法上 → 锁的是类对象(Counter.class),不是实例,和你想保护的this毫无关系 - 多个线程操作的是不同实例 → 即使方法是
synchronized,也毫无互斥作用 - 在方法内部修改了
this引用(比如this = new Counter())→ 语法错误,Java 不允许赋值给this,但有人误以为能“换锁”,其实根本不可行 - 同步方法里调用了非同步的 setter/getter,而这些方法又被外部直接调用 → 锁保护不完整,数据仍可能被并发修改
和 ReentrantLock 相比有什么关键差异
两者都能实现对实例的独占访问,但行为逻辑不同:
-
synchronized是 JVM 层原语,自动释放锁(即使抛异常),无需手动管理;ReentrantLock必须配对调用lock()/unlock(),且unlock()最好放在finally块里 -
synchronized不支持超时、中断响应、公平锁等高级特性;ReentrantLock可以 - 锁升级机制:HotSpot 中,
synchronized在低竞争时用偏向锁/轻量级锁,开销极小;高竞争才膨胀为重量级锁 - 调试时,jstack 能清晰看到
synchronized的锁持有者和等待线程;ReentrantLock的堆栈信息稍弱一些
除非你需要可中断等待或尝试获取锁,否则优先用 synchronized 实例方法——它更轻量、更安全、也更符合 Java 的直觉模型。










