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>嗯,用户现在想了解Javasynchronized锁的可重入及其原理。根据之前的对话历史,用户已经接触过Seata协议的相关内容,但现在转向了Java并发的问题。需要确保回答准确且符合用户提供的引用内容。 首先,用户的问题很明确:synchronized是否可重入,以及原理是什么。引用中的资料提到synchronized可重入的,并且解释了可重入锁的实现原理,比如通过记录持有线程和持有数量。比如引用[3]和引用[5]都提到这一点。 我需要先确认用户可能已经知道什么。用户可能已经了解synchronized的基本用法,比如修饰代码块和方法,但可能对可重入的具体机制不太清楚。用户提供的引用中有示例代码,比如引用[2]中的ReentrantLockExample,展示了方法A和方法B都使用同一个锁的情况,说明同一线程可以多次获取锁,这就是可重入的体现。 接下来,需要组织回答的结构。根据用户之前的回答风格,分点回答会比较清晰。例如,先回答是否可重入,再解释原理,接着与ReentrantLock的对比,最后总结应用场景。同时要引用用户提供的引用资料来支持观点。 需要注意用户提供的引用中有一些关于Reentrant Lock的对比,比如引用[4]提到synchronized是隐式获取释放锁,而ReentrantLock需要显式调用。但在回答synchronized可重入时,可能不需要深入对比,但可以简要提及,以帮助理解不同锁的实现差异。 另外,用户提供的引用[5]详细说明了synchronized的实现原理,包括锁对象头中的计数器和持有线程信息。这部分需要转化为更易懂的语言,避免过于技术化的术语,但保持准确。 可能存在的疑问点:用户是否了解对象头、Monitor等底层概念?需要适当解释,但不要过于深入,以免混淆。例如,可以提到JVM通过对象头中的计数器来跟踪锁的重入次数,而不必展开对象头的具体结构。 还要检查是否有遗漏的信息。例如,synchronized可重入是否有限制?根据资料,同一线程可以多次获取同一锁,没有限制次数,但需要正确释放,即每次进入同步块计数器加一,退出时减一,直到为零才释放锁。这点需要明确说明。 最后,生成相关问题部分,需要考虑用户可能进一步的问题,比如与ReentrantLock的区别,或者实际应用中的注意事项。根据引用中的内容,可以提出关于实现原理对比、应用场景选择等问题,帮助用户深入理解。</think>### Javasynchronized锁的可重入及原理分析 #### 一、synchronized是否可重入? **是的,synchronized锁是可重入的**。 在Java中,同一线程可以多次获取同一个对象锁,而不会因锁已被自身持有而阻塞。例如以下场景均可正常执行: 1. 同一线程递归调用`synchronized`方法。 2. 同一线程在`synchronized`代码块内调用另一个`synchronized`方法(锁对象相同)。 3. 子类通过`synchronized`方法调用父类的`synchronized`方法(锁对象为实例本身)[^3]。 **示例**(基于引用[2]的代码扩展): ```java public class ReentrantDemo { public synchronized void methodA() { methodB(); // 同一线程可重入锁 } public synchronized void methodB() { System.out.println("Reentrant call"); } } ``` 上述代码中,线程调用`methodA`时会自动获取实例锁,并在调用`methodB`时再次获取同一锁而不会阻塞。 --- #### 二、synchronized可重入的实现原理 ##### 1. **锁的持有者与计数器** - **对象头标记**:每个Java对象都与一个监视器(Monitor)关联,对象头中存储了锁状态、持有线程和重入计数器[^5]。 - **计数器机制**: - 当线程首次获取锁时,计数器置为1。 - 每次重入(同一线程再次获取锁),计数器**递增**。 - 退出同步代码块时,计数器**递减**,直至归零时释放锁[^5]。 ##### 2. **源码级逻辑** ```java // 伪代码示意 if (当前线程 == 锁持有线程) { 计数器++; } else { 竞争锁或加入等待队列; } ``` ##### 3. **关键设计特点** - **粒度控制**:锁的获取以**线程**为粒度,而非方法调用(即“per-thread”而非“per-invocation”)[^3]。 - **避免死锁**:可重入避免了线程因重复请求自身已持有的锁而导致的死锁。 --- #### 三、与ReentrantLock的对比 虽然两者均支持可重入,但实现方式不同: | 特 | synchronized | ReentrantLock | |--------------------|----------------------------------|------------------------------| | **锁获取/释放** | 自动(进入/退出代码块时触发)[^4] | 手动(`lock()`/`unlock()`)[^4] | | **实现机制** | JVM内置锁,通过对象头实现 | 基于AQS(AbstractQueuedSynchronizer) | | **灵活** | 有限 | 支持公平锁、条件变量等 | --- #### 四、应用场景与注意事项 1. **适用场景**: - 方法递归调用。 - 多层同步代码块的嵌套执行(如调用链中的多个同步方法)。 2. **风险提示**: - **锁泄漏**:若重入次数未正确释放(如异常跳过`monitorexit`),可能导致锁无法释放。 - **能影响**:过深的锁重入可能增加竞争概率,需合理设计同步范围。 --- ### 相关问题 1. **synchronized可重入与ReentrantLock的实现机制有何本质区别?** 2. **若synchronized的计数器溢出会发生什么?Java如何避免这种情况?** 3. **在分布式系统中,synchronized可重入是否仍然有效?为什么?** 4. **如何通过JVM工具(如jstack)观察synchronized锁的重入次数?**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值