[java 锁 - 03 重入写法 ]
可重入锁的核心是“同一线程可重复获取同一把锁”,Java 中 synchronized 和 ReentrantLock 都是可重入锁,写法各有特点。下面分别两种锁的可重入用法示例,清晰展示“重复获取锁”的场景:
一、synchronized 可重入写法(隐式可重入,JVM 自动管理)
synchronized 天生支持可重入,同一线程在持有锁时,调用同一锁对象的其他同步方法/代码块,会自动重复获取锁(无需手动操作)。
示例:同步方法的可重入
public class SynchronizedReentrantDemo {
// 锁对象(当前实例 this)
public synchronized void methodA() {
System.out.println("进入 methodA(已获取锁)");
// 同一线程调用同一锁的 methodB(可重入)
methodB();
System.out.println("退出 methodA(释放部分锁计数)");
}
// 同一锁对象(this)的同步方法
public synchronized void methodB() {
System.out.println("进入 methodB(重复获取锁,锁计数+1)");
// 还可以继续调用其他同步方法(如 methodC),依然可重入
System.out.println("退出 methodB(锁计数-1)");
}
public static void main(String[] args) {
SynchronizedReentrantDemo demo = new SynchronizedReentrantDemo();
// 启动线程调用 methodA
new Thread(demo::methodA).start();
}
}
执行结果:
进入 methodA(已获取锁)
进入 methodB(重复获取锁,锁计数+1)
退出 methodB(锁计数-1)
退出 methodA(释放部分锁计数)
原理:
JVM 会为 synchronized 锁维护一个“重入计数器”:
- 线程首次获取锁时,计数器=1;
- 同一线程再次获取同一锁时,计数器+1;
- 线程退出同步块/方法时,计数器-1;
- 当计数器=0 时,锁才真正释放,其他线程可获取。
示例:同步代码块的可重入
public class SynchronizedBlockReentrantDemo {
private final Object lock = new Object(); // 自定义锁对象
public void methodX() {
synchronized (lock) { // 首次获取 lock 锁(计数器=1)
System.out.println("进入 methodX(已获取锁)");
methodY(); // 同一线程调用同一锁的 methodY
System.out.println("退出 methodX(计数器-1)");
}
}
public void methodY() {
synchronized (lock) { // 重复获取 lock 锁(计数器=2)
System.out.println("进入 methodY(锁计数器+1)");
System.out.println("退出 methodY(计数器-1)");
}
}
public static void main(String[] args) {
new Thread(new SynchronizedBlockReentrantDemo()::methodX).start();
}
}
执行结果:
进入 methodX(已获取锁)
进入 methodY(锁计数器+1)
退出 methodY(计数器-1)
退出 methodX(计数器-1)
二、ReentrantLock 可重入写法(显式可重入,手动控制)
ReentrantLock 是显式可重入锁,需手动调用 lock() 获取锁、unlock() 释放锁,重入时同样会增加计数器,释放时需对应减少计数器(lock() 和 unlock() 次数必须相等)。
示例:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock(); // 可重入锁
public void method1() {
lock.lock(); // 首次获取锁(计数器=1)
try {
System.out.println("进入 method1(已获取锁)");
method2(); // 同一线程调用 method2,重复获取锁
} finally {
lock.unlock(); // 释放一次(计数器=0,锁真正释放)
System.out.println("退出 method1(释放锁)");
}
}
public void method2() {
lock.lock(); // 重复获取锁(计数器=2)
try {
System.out.println("进入 method2(锁计数器+1)");
} finally {
lock.unlock(); // 释放一次(计数器=1)
System.out.println("退出 method2(释放部分锁)");
}
}
public static void main(String[] args) {
new Thread(new ReentrantLockDemo()::method1).start();
}
}
执行结果:
进入 method1(已获取锁)
进入 method2(锁计数器+1)
退出 method2(释放部分锁)
退出 method1(释放锁)
关键注意:
ReentrantLock 的 lock() 和 unlock() 必须成对出现,重入几次就要释放几次,否则会导致锁无法真正释放(比如重入2次但只释放1次,计数器=1,其他线程永远无法获取锁)。
三、可重入锁的核心价值
避免“线程自我阻塞”:如果锁不可重入,同一线程在 methodA 中获取锁后,调用 methodB 时会因再次请求同一锁而阻塞(自己等自己),导致死锁。可重入锁通过计数器机制解决了这一问题,确保同一线程能流畅执行嵌套的同步逻辑(如事务嵌套、递归加锁等场景)。
总结
synchronized:隐式可重入,无需手动管理计数器,适合简单场景。ReentrantLock:显式可重入,需手动控制lock()/unlock()次数,适合需要公平锁、超时等高级特性的场景。
两种锁的可重入性都依赖“计数器”机制,核心是“同一线程重复获取锁时不阻塞,释放对应次数后才真正释放锁”。

浙公网安备 33010602011771号