死锁★★★★
死锁★★★★
声明:
该部分内容参考自CSDN博主Hyacinth_Dy原创文章,仅用作个人学习使用,特此声明
本文链接:https://blog.csdn.net/jyy305/article/details/70077042
1、产生条件(必要)
互斥条件
:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
不可剥夺条件
:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
请求和保持条件
:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
循环等待条件
:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所申请的资源。
以上给出了导致死锁的四个必要条件,只要系统发生死锁则以上四个条件至少有一个成立。事实上循环等待的成立蕴含了前三个条件的成立,似乎没有必要列出。然而考虑这些条件对死锁的预防是有利的,因为可以通过破坏除资源互斥条件以外的另外三个条件中的任何一个来预防死锁的发生。
2、死锁预防
由于资源互斥是资源使用的固有特性,是无法改变的,我们可以通过破坏死锁产生的其他3个必要条件来预防死锁。
破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。大白话来讲就是一大堆进程苦哈哈的等着,先凑够谁的全部资源就赶紧把这些资源都给这个进程
破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
破坏“循环等待”条件:采用资源有序分配。其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的资源才能申请较大编号的资源。也就是说你先别干资源紧缺的大事,先把你手头上的资源不紧缺的小事做完,才有资格申请紧缺的资源去做剩下的大事
3、死锁避免
死锁避免的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。
如果操作系统能保证所有进程在有限时间内得到需要的全部资源,则系统处于安全状态否则系统是不安全的。
安全状态:如果系统存在所有的安全序列{P1,P2,…Pn},则系统处于安全状态。一个进程序列是安全的,对其中每一个进程Pi来讲,如果他以后尚需要的资源不超过系统当前剩余资源量与所有进程Pj(j >i)当前占有资源量之和,系统处于安全状态则不会发生死锁。
不安全状态:如果不存在任何一个安全序列,则系统处于不安全状态。他们之间的对对应关系如下图所示:
只看文字理解系统是否处于安全状态是很困难的,我们通过举例来理解就会很清晰明朗了
如上图所示系统处于安全状态,系统剩余3个资源,可以把其中的2个分配给P3,此时P3已经获得了所有的资源,执行完毕后还能还给系统4个资源,此时系统剩余5个资源所以满足(P2所需的资源不超过系统当前剩余量与P3当前占有资源量之和),同理P1也可以在P2执行完毕后获得自己需要的资源。
如果P1提出再申请一个资源的要求,系统从剩余的资源中分配一个给进程P1,此时系统剩余2个资源,新的状态图如下:那么是否仍是安全序列呢那我们来分析一下
系统当前剩余2个资源,分配给P3后P3执行完毕还给系统4个资源,但是P2需要5个资源,P1需要6个资源,他们都无法获得资源执行完成,因此找不到一个安全序列。此时系统转到了不安全状态。
4、用代码实现死锁
/**
* 比如:t1想先穿衣服再穿裤子
* t2想先穿裤子再穿衣服
* 此时:t1拿到衣服,t2拿到裤子;
* 由于t1拿了衣服,t2找不到衣服;t2拿了裤子,t1找不到裤子
* 就会导致死锁的发生!
*/
public class Thread_DeadLock {
public static void main(String[] args) {
Dress dress = new Dress();
Trousers trousers = new Trousers();
//t1、t2共享dress和trousers。
Thread t1 = new Thread(new MyRunnable1(dress, trousers), "t1");
Thread t2 = new Thread(new MyRunnable2(dress, trousers), "t2");
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable{
Dress dress;
Trousers trousers;
public MyRunnable1() {
}
public MyRunnable1(Dress dress, Trousers trousers) {
this.dress = dress;
this.trousers = trousers;
}
@Override
public void run() {
synchronized(dress){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (trousers){
System.out.println("--------------");
}
}
}
}
class MyRunnable2 implements Runnable{
Dress dress;
Trousers trousers;
public MyRunnable2() {
}
public MyRunnable2(Dress dress, Trousers trousers) {
this.dress = dress;
this.trousers = trousers;
}
@Override
public void run() {
synchronized(trousers){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (dress){
System.out.println("。。。。。。。。。。。。。。");
}
}
}
}
class Dress{
}
class Trousers{
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律