重入原理:
- 每个锁关联一个计数器和一个持有线程。
- 当线程首次获取锁时,计数器置为1,线程成为锁的持有者。
- 同一线程每次重入,计数器递增;退出同步代码块时计数器递减。
- 计数器归零时,锁被释放,其他线程可竞争。
锁重入 就像你进自家大门:
- 你第一次用钥匙开门(获取锁),进入房间(执行同步代码)。
- 此时你在房间里又打开卧室门(调用另一个
synchronized
方法),不需要重新掏钥匙,因为整个房子(对象锁)已经被你独占。 - 等你从卧室出来(退出内层方法),再退出大门(退出外层方法),锁才完全释放。
public class Home {
public synchronized void enterFrontDoor() { // 进大门
System.out.println("进了大门");
enterBedroom(); // 直接进卧室,不用重新开锁!
}
public synchronized void enterBedroom() { // 进卧室
System.out.println("进了卧室");
}
public static void main(String[] args) {
Home home = new Home();
new Thread(home::enterFrontDoor).start(); // 同一线程畅通无阻
}
}
关键:
-
无需竞态阻塞:
- 同一线程内,外层方法(
enterFrontDoor
)已经持有了对象锁(Home
实例的锁)。 - 内层方法(
enterBedroom
)发现锁的持有者是当前线程,直接放行,不会触发线程竞争。
- 同一线程内,外层方法(
-
锁计数器机制:
- JVM为每个锁维护一个计数器:
- 第一次获取锁(进大门)→ 计数器=1
- 重入调用内层同步方法(进卧室)→ 计数器+1 → 计数器=2
- 退出内层方法 → 计数器-1 → 计数器=1
- 退出外层方法 → 计数器-1 → 计数器=0 → 真正释放锁
- 只有计数器归零时,其他线程才能竞争锁。
- JVM为每个锁维护一个计数器:
-
对比“不可重入锁”:
- 如果锁不可重入,当你在
enterFrontDoor
中调用enterBedroom
时:- 外层方法已持有锁,内层方法尝试获取同一把锁 → 死锁!(线程自己卡死自己)
- 如果锁不可重入,当你在