JAVA可重入锁死锁

本文介绍了JAVA中的可重入锁概念及其工作原理,指出可重入锁能防止死锁的发生。同时,文章详细阐述了死锁产生的四个必要条件,并提供了一个简单的死锁案例。最后,讨论了避免死锁的两种策略:加锁顺序和加锁时限,并提到了通过jconsole等工具进行死锁检测的方法。

可重入锁

可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的
synchronized 和 ReentrantLock 都是可重入锁。
可重入锁的意义之一在于防止死锁
实现原理实现是通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 。
如果同一个线程再次请求这个锁,计数器将递增;
每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。
关于父类和子类的锁的重入:子类覆写了父类的synchonized方法,然后调用父类中的方法,此时如果没有可重入的锁,那么这段代码将产生死锁(很好理解吧)。

例子:

比如说A类中有个方法
public synchronized methodA1(){
   
   

methodA2();
}

public synchronized methodA2(){
   
   
                    //具体操作
}

也是A类中的同步方法,当当前线程调用A类的对象methodA1同步方法,如果其他线程没有获取A类的对象锁,那么当前线程就获得当前A类对象的锁,然后执行methodA1同步方法,方法体中调用methodA2同步方法,当前线程能够再次获取A类对象的锁,而其他线程是不可以的,这就是可重入锁
如果锁的计数器为2,执行过程中抛出异常,锁的计数器直接置为0,会直接释放锁!
应该是一个线程,如果执行同步代码块过程中抛出异常未捕获,会立即终止,退出同步代码块,并且释放锁,不会执行后续代码。
最核心的就是抛了异常,线程内部如果没处理,线程会直接停止!

死锁

在java中我们常常使用加锁机制来确保线程安全,但是如果过度使用加锁,则可能导致锁顺序死锁。同样,我们使用线程池和信号量来限制对资源的使用,但是这些被限制的行为可能会导致资源死锁。java应用程序无法从死锁中恢复过来,因此设计时一定要排序那些可能导致死锁出现的条件。

产生死锁的条件

产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。
(1)互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
(2)不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
(3)请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
(4

<think>我们正在讨论可重入的定义。根据引用[1][2][3]的内容,可重入是指同一个线程可以多次获得同一把,而不会导致死机制。其核心在于内部维护一个持有计数(hold count)和持有线程的标识。当线程第一次获取时,计数为1;同一线程再次获取时,计数递增;释放时,计数递减,直到计数为0时才完全释放。 </think>### 可重入的定义 可重入(Reentrant Lock)是一种允许**同一个线程多次获取同一把**的同步机制。其核心特性是: 1. **递归获取** 线程在持有期间可重复调用加操作而不会自锁死 - 内部维护持有计数(`hold count`) - 首次获取:持有计数=1 - 同一线程再次获取:持有计数+N - 释放时:计数逐次递减,归零时完全释放资源[^1][^2] 2. **线程标识绑定** 记录当前持有线程的标识(如 `Thread ID`) $$\text{状态} = (\text{持有线程}, \text{持有计数})$$ 当其他线程请求时直接阻塞,当前线程请求则计数递增[^3] 3. **避免自死** 经典场景:同步方法递归调用 ```java public synchronized void methodA() { methodB(); // 可重入允许进入(持有计数=2) } public synchronized void methodB() { /* 操作 */ } // 无需等待释放 ``` 若为不可重入:`methodA` 未释放时调用 `methodB` 将永久阻塞[^4] --- ### 关键特性对比 | **特性** | 可重入 | 不可重入 | |------------------|------------------------|------------------------| | 递归调用 | ✅ 允许同一线程多次获取 | ❌ 导致死 | | 持有计数 | ✅ 维护计数状态 | ❌ 无状态记录 | | 释放机制 | 计数归零时释放 | 立即释放 | | 实现复杂度 | 需绑定线程ID和计数器[^4] | 只需布尔状态标记 | --- ### 实现原理(以AQS为例) ```java // 简化版可重入实现 class ReentrantLock { private Thread owner; // 持有者 private int holdCount = 0; // 持有计数 public synchronized void lock() { Thread current = Thread.currentThread(); if (owner == current) { holdCount++; // 重入时计数+1 } else { while (holdCount != 0) wait(); // 其他线程等待 owner = current; holdCount = 1; } } public synchronized void unlock() { if (--holdCount == 0) { owner = null; // 计数归零时释放 notifyAll(); } } } ``` > 当线程首次获取时,设置 `owner = currentThread`, `holdCount=1`;重入时仅递增计数,释放时递减至0才唤醒其他线程[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值