java内置锁synchronized的可重入性

本文详细解释了Java内置锁synchronized的可重入性概念,阐述了如何通过避免死锁提升并发编程效率。通过实例代码演示,展示了在多线程环境下,同一个线程多次获取同一把锁的操作流程,以及这种特性如何降低发生死锁的概率。

当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞。然而,由于java内置锁synchronized是可重入的,因此如果某个线程试图获得一个已经有它自己持有的锁,那么这个请求就会成功。如代码:

public class Widget {
    public synchronized void doSomething() {
        ...
    }
}
 
public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        System.out.println(toString() + ": calling doSomething");
        super.doSomething();//若内置锁是不可重入的,则发生死锁
    }
}

1.重入

“重入”意味着获取锁操作的粒度是“线程”,而不是“调用”。

2.重入的实现方式

为每个锁关联一个获取计数值和一个所有者线程。当计数值为0的时,就认为这个锁是没有被任何线程持有。当线程请求一个未被持有的锁时,对于内置锁synchronized,jvm将记下锁的持有者,并且将获取计数值置为1。如果同一个线程再次获取这个锁,计数值将递增,而当线程线程推出同步代码块时,计数值将递减。当计数值为0时,这个锁将被释放。

3.java的synchronized关键字

java内置锁synchronized的可重入性有jvm保证的,这个提升了加锁行为的封装性,从而简化了java并发编程的开发。但Tiger后提供了可重入锁ReentrantLock是自己实现可重入性的,而不是由jvm保证的。关于ReentrantLock会在后面介绍的。

4.java内置锁synchronized的可重入性可以降低deadLock的情况发生,如上面的事例代码。


参考书籍:《java并发编程实战》

<think> 我们正在讨论Java中的synchronized关键字是否为可重入锁。根据引用内容,特别是引用[1]、[2]、[4]和[5]都明确指出synchronized可重入锁。 引用[1]解释了如果锁不可重入会发生什么:线程在持有锁的情况下再次请求同一把锁时会被阻塞,导致死锁。而synchronized避免了这种情况。 引用[2]直接说明synchronizedJava的内置可重入锁。 引用[4]详细说明了synchronized通过锁计数器实现可重入:每次进入锁计数器加1,退出时减1,直到0才释放锁。 引用[5]也提到重入锁(ReentrantLock)和synchronized都是可重入的。 因此,答案是肯定的:synchronized可重入锁。 下面我们详细解释一下: </think> ### `synchronized` 是可重入锁吗? 是的,**Java 中的 `synchronized` 是可重入锁**。这是 Java 锁设计的核心特,通过以下机制实现: --- #### 一、可重入锁的核心原理 1. **锁计数器机制** `synchronized` 内部维护一个锁计数器(`lock count`): - 线程首次获取锁时:计数器设为 **1** - 同一线程重入锁时:计数器 **+1** - 退出同步块时:计数器 **-1** - 释放锁时:计数器归 **0** 数学表示: $$ \text{LockCount} = \begin{cases} 1 & \text{首次获取锁} \\ \text{LockCount} + 1 & \text{重入锁} \\ \text{LockCount} - 1 & \text{退出同步块} \end{cases} $$ 当 $\text{LockCount} = 0$ 时,锁被完全释放。[^4] 2. **不可重入锁的缺陷** 若锁不可重入(如引用[3]的手写锁),线程在已持锁时再次请求锁会**永久阻塞**: ```java class Lock { private boolean isLocked = false; // 不可重入锁实现 public synchronized void lock() throws InterruptedException { while (isLocked) wait(); // 第二次请求时:自己等待自己释放锁 → 死锁 isLocked = true; } } ``` --- #### 二、`synchronized` 的重入验证 通过同步方法嵌套调用验证: ```java public class ReentrantDemo { public synchronized void methodA() { System.out.println("Enter methodA"); methodB(); // 嵌套调用需要同一把锁 } public synchronized void methodB() { System.out.println("Enter methodB"); // 若不可重入,此处会阻塞 } public static void main(String[] args) { ReentrantDemo demo = new ReentrantDemo(); demo.methodA(); // 输出两行日志 → 证明可重入 } } ``` **输出结果**: ``` Enter methodA Enter methodB // 成功执行,无阻塞 ``` --- #### 三、`synchronized` vs `ReentrantLock` 的重入对比 | **特** | `synchronized` | `ReentrantLock` | |------------------|------------------------------------|-------------------------------| | **重入** | 内置支持 | 显式支持(继承`Lock`接口) | | **锁获取方式** | JVM 隐式管理 | 手动调用 `lock()`/`unlock()` | | **实现复杂度** | 简单(编译器插入字节码) | 复杂(基于`AQS`实现) | | **锁计数器存储** | 对象头中的 `Mark Word` | `AQS` 中的 `state` 变量 | | **底层指令** | `monitorenter` 和 `monitorexit`[^4]| `CAS` 操作 | > 💡 两者都是可重入锁,但 `synchronized` 通过 JVM 内置机制实现,而 `ReentrantLock` 通过代码显式实现。 --- #### 四、不可重入锁的致命问题:自我死锁 若 `synchronized` 不可重入,以下场景将发生死锁: ```mermaid graph TD A[线程T获取锁] --> B[执行同步方法method1] B --> C[调用同步方法method2] C --> D{尝试获取同一把锁} D -->|锁不可重入| E[线程T阻塞等待释放锁] E --> F[但锁被线程T自身持有] F --> G[永久阻塞 → 死锁] ``` --- ### 总结 1. ✅ **`synchronized` 是可重入锁**,通过锁计数器避免线程自我阻塞 2. 🔒 重入解决了**同步方法嵌套调用**的死锁问题 3. ️ 底层通过 `monitorenter` 和 `monitorexit` 指令实现[^4] 4. ⚖️ 与 `ReentrantLock` 的主要区别在于**隐式/显式管理**,而非重入 [^1]: 不可重入锁导致线程自我死锁的机制 [^3]: 不可重入锁的代码实现缺陷 [^4]: synchronized 通过锁计数器实现可重入 [^5]: ReentrantLock 与 synchronized 的重入对比
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值