synchronized 的可重入性:避免死锁的隐藏武器

摘要

synchronized 关键字具备 可重入性 (Reentrancy),同一线程在持有锁的情况下,可以再次获取同一把锁而不会阻塞。本文将从概念、代码示例、JVM 实现机制和工程实践四个方面,深入解析 synchronized 的可重入性。


正文

一、什么是可重入性?

可重入性(Reentrant)是指 同一线程在持有锁时,可以重复进入被同一个锁保护的临界区,而不会发生死锁。

换句话说:如果锁是"可重入"的,那么一个线程在进入某个 synchronized 方法/代码块后,还能继续调用另一个需要同一把锁的 synchronized 方法。


二、代码示例

1. 方法递归调用
java 复制代码
public class ReentrantDemo {
    public synchronized void methodA() {
        System.out.println(Thread.currentThread().getName() + " 进入 methodA");
        methodB(); // 再次请求同一个锁
    }

    public synchronized void methodB() {
        System.out.println(Thread.currentThread().getName() + " 进入 methodB");
    }

    public static void main(String[] args) {
        ReentrantDemo demo = new ReentrantDemo();
        new Thread(demo::methodA, "T1").start();
    }
}

结果:线程 T1 先进入 methodA,随后 不会阻塞 ,而是直接进入 methodB,因为这是同一线程在请求同一把对象锁。

2. 父子类继承中的可重入性
java 复制代码
class Parent {
    public synchronized void doSomething() {
        System.out.println("父类方法执行");
    }
}

class Child extends Parent {
    @Override
    public synchronized void doSomething() {
        System.out.println("子类方法执行");
        super.doSomething(); // 依然是同一把锁
    }
}

结果:子类方法调用父类方法不会造成死锁,因为锁是可重入的。


三、JVM 如何实现可重入性?

在 JVM 中,synchronized 是基于 对象头中的 Monitor(监视器锁) 实现的。

Monitor 内部维护了一个 计数器(recursions) ,用于记录线程重入的次数。

  • 第一次获取锁:计数器从 0 → 1,线程成为锁的拥有者。
  • 同一线程再次获取锁:计数器 +1。
  • 退出同步块/方法:计数器 -1。
  • 计数器归零:锁才真正释放,其他线程才能获取。

这就是为什么 synchronized 能避免 同一线程自我阻塞 的根本原因。


四、可重入性的优势

  1. 避免死锁
  • 如果没有可重入性,线程在调用 methodA() 的时候进入 methodB() 就会死锁。
  1. 提升编程灵活性
  • 允许在继承、递归调用中安全地使用 synchronized
  1. 降低锁使用的复杂度
  • 开发者无需手动判断当前线程是否持有锁,简化了并发编程模型。

五、工程实践建议

  1. 理解可重入,但不要滥用
  • 可重入不是性能优化手段,而是一种安全保证。
  • 不要因为"可重入"就随意嵌套锁,仍需控制临界区大小。
  1. 注意递归调用的深度
  • 虽然可重入避免了死锁,但无限递归依然可能导致栈溢出。
  1. 对比 ReentrantLock
  • ReentrantLock 也支持可重入,并提供了更多功能(可中断、公平锁、条件队列)。
  • 在需要灵活控制时,可以考虑替代 synchronized

六、总结

synchronized 的可重入性是并发编程中的重要特性:

  • 同一线程可以重复进入锁保护的代码,不会造成自我死锁;
  • 底层通过 Monitor 的计数器 实现;
  • 在递归、继承等复杂场景下尤为重要。

理解可重入性,可以帮助我们在设计并发程序时写出更加 安全、简洁、易维护 的代码。

相关推荐
二闹3 小时前
Python字符串格式化:谁才是真正的硬汉?
后端·python
少JSQ3 小时前
创建一个对象时发生什么
java·jvm
Livingbody3 小时前
零代码实践自然语言处理 - 文本分类任务实现,以qwen模型和OpenAI SDK为例
后端
一叶飘零_sweeeet3 小时前
SpringBoot 整合 Kafka 的实战指南
java·spring boot·kafka
间彧3 小时前
Java Stream reduce方法深度解析
java
FrankYoou3 小时前
spring boot autoconfigure 自动配置的类,和手工 @configuration + @bean 本质区别
java·spring boot·后端
lovebugs3 小时前
JVM内存迷宫:破解OutOfMemoryError的终极指南
java·后端·面试
Swift社区3 小时前
66项目中 Spring Boot 配置文件未生效该如何解决
java·spring boot·后端
零雲3 小时前
66java面试:可以讲解一下mysql的索引吗
java·mysql·面试