死锁

什么是死锁?

信号量引入了一种潜在的令人厌恶的运行错误,叫做死锁,它是指一组线程被阻塞了,等待一个永远也不会为真的条件。进度图对于理解死锁是一个无价的工具,下图展示了一对用两个信号量来实现互斥的线程的进程图:
这里写图片描述
从这幅图中我们能够得到一些关于死锁的重要知识:

    (1)程序员使用P和V操作顺序不当,以至于两个信号量的禁止区域重叠。如果某个执行轨迹线碰巧到达了死锁状态d,那么就不可能有进一步的进展了,因为重叠的禁止区域阻塞了每个合法方向上的进展。换句话说,程序死锁是因为每个线程都在等待其他线程执行一个根本不可能发生的V操作。
    (2)重叠的禁止区域引起了一组称为死锁区域的状态。如果一个轨迹线碰巧达到了一个死锁区域中的状态,那么死锁就是不可避免的了。轨迹线可以进入死锁区域,但是他们不可能离开。
    (3)死锁是一个相当困难的问题,因为它不总是可预测的。一些幸运的执行轨迹线将绕开死锁区域,而其他的将会陷入这个区域。

产生死锁的原因

(1)竞争资源。当系统中供多个进程共享的资源如打印机、共用队列等,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁。
(2)进程间推进顺序非法。进程在运行过程中,强求和释放资源的顺序不当,也同样会导致进程死锁。

产生死锁的必要条件

(1)互斥条件:指进程对所分配的资源进行排它性使用,即在一段时间内某资源只占由一个进程占用。如果此时患有其他进程请求该资源,则请求者只能等待,直至占有该资源的进程用毕释放。
(2)请求和保持条件:值进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又已被其他进程占有,此时请求进程阻塞,但又对自己获得的其他资源保持不放。
(3)不剥夺条件:指进程已获得的资源,在为使用完之前,不能被剥夺,只能在使用完时由自己释放。
(4)环路等待条件:值在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2...,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源...,Pn正在等待已被P0占用的资源。

处理死锁的基本方法

(1)预防死锁。这是一种比较简单和直观的事先预防的方法。该方法是通过设置某些限制条件,去破坏产生死锁的4个必要条件中的一个或几个条件,来预防死锁的发生。
(2)避免死锁。该方法也是属于事先预防的策略。但它并不是事先采取各种限制条件措施去破坏产生4个必要条件,而是在资源动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。
(3)检测死锁。该种方法是允许系统在运行过程中发生死锁,但可以通过系统所设置的检测机构,及时的检测死锁发生,并精确的确定与死锁有关的进程和资源,然后,采取适当措施,从系统中奖已发生的死锁清除掉。
(4)解除死锁。与检测相配套,当检测到系统中发生死锁时,需要将进程从死锁中解脱出来。

预防死锁

(1)摒弃“请求和保持”条件

在采用这种方法时,系统规定所有进程在开始运行之前,都必须一次性地申请其在整个运行过程所需的全部资源。此时,系统有足够的资源分配给某进程,便可以把其需要的所有资源分配给该进程,这样,该进程在运行的时候就不会再提出资源要求,从而摒弃了请求要求。但在分配资源的时,只有一种资源不能满足某进程的要求时,即使其他所需的各资源都空闲,也不会分配给该进程,而让进程等待,从而摒弃了保持条件,可以避免死锁。
这种预防死锁的方法其优点是简单、易于实现且安全,但是缺点也很明显:首先先表现为资源严重被浪费,其次是进程延迟运行。

(2)摒弃“不剥夺”条件

在采用这种方法时,系统规定,进程是诸葛地提出对资源的要求的,当一个一级保持了某些资源的进程,在提出新的资源请求而不能立即得到猫族时,必须释放它一级保持了的所有资源,在以后使用时再重新申请。这意味着某一进程已经战役后的资源,在运行过程中会被短暂时释放,也可认为是被剥夺了,从而摒弃了“不剥夺”条件。
这种预防死锁的方法实现起来比较复杂且付出很大的代价,此外,这种策略 还可能因为反复的申请和释放资源。导致进程的执行被无线的推迟,这不仅延长了进程的周转时间,也怎建看系统的开销,降低了系统的吞吐量。

(3)摒弃“环路等待”条件

这种方法中规定,系统将所有的资源按类型进行线性排队,并赋予不同的序号。所有的进程对资源的请求必须严格按照资源序号递增的次序提出,这样在所形成的资源分配图中,不可能在出现环路,因而摒弃了“环路等待”条件。
这种预防死锁的策略与前两种比较,其资源利用率和系统吞吐量都有比较明显的改动,但也有问题:(1)为系统中各类资源所分配的序号必须相对稳定,(2)作业(进程)使用各类资源的顺序与系统规定的顺序不同,造成资源浪费,(3)为方便用户,系统对用户在编程时所施加的限制条件应尽量少,然而这种按规定次序申请的方法,必然会限制用户简单、自主地编程。

避免死锁

在避免死锁的方法中,所施加的限制条件较弱,有可能获得令人满意的系统性能,在该方法中国吧系统的状态分为安全状态和不安全状态,只要能使系统始终都处于安全状态,便可避免发生死锁。

安全状态

在避免死锁的方法中国,允许进程动态的申请资源,但系统在进行资源分配之前,应先计算此次资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程,否则令进程等待。

死锁的检测

当系统为进程分配资源时,若未任何限制性措施,则系统必须提供检测和接触死锁的手段,为此,系统必须做到:
(1)保存有关资源的请求和分配信息,(2)提供一种算法,以利用这些信息来检测系统是否已进入死锁状态;

(1)资源分配图,(2)死锁定理,(3)死锁检测中的数据结构

死锁的解除

(1)剥夺资源:从其他进程剥夺足够数量的资源给死锁进程,一解除死锁状态;
(2)撤销进程:最见到的撤销方法是使全部死锁检测都夭折掉;稍微温和一点的方法是按照某种顺序诸葛地撤销进程,直至有足够的资源可用,使死锁状态消除为止。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是指两个或多个进程在互斥地请求资源的时候,由于资源被占用而无法继续执行,导致所有进程都被阻塞的情况。在Spring Boot中,可能发生在多线程并发访问共享资源时,例如数据库连接池、缓存、等。 在给出解决方案之前,我们需要先排查问题。一种常用的排查问题的方法是使用jstack命令输出线程的堆栈信息。通过查看堆栈信息,我们可以定位到可能引起的代码行,并进行解决修复。 下面是一个示例代码,模拟了一个可能导致的场景: ```java package com.xz.springboottest.day1; public class DeadLock { private static final String ACTION_ONE = "拿起碗"; private static final String ACTION_TWO = "拿起筷子"; public static void main(String[] args) { // 哲学家小明 new Thread(() -> { synchronized (ACTION_ONE) { try { Thread.sleep(1000); synchronized (ACTION_TWO) { System.out.println("小明开始吃饭了。。。。。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); // 哲学家小张 new Thread(() -> { synchronized (ACTION_TWO) { try { Thread.sleep(1000); synchronized (ACTION_ONE) { System.out.println("小张开始吃饭了。。。。。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } ``` 以上代码模拟了哲学家就餐的场景,当只剩下一只碗和一双筷子时,可能出现的问题。 对于解决的方法,可以考虑以下几种方案: 1. 避免循环等待:为了避免,可以规定所有线程在请求资源时按照固定的顺序获取,从而避免循环等待。 2. 加顺序:在多个线程同时请求多个资源的情况下,为了避免,可以约定线程必须按照相同的顺序请求资源。 3. 设置超时时间:在获取的时候设置超时时间,如果超过一定时间还未获取到,可以放弃或者重试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值