死锁的四个必要条件(图文详解)

死锁的四个必要条件(图文详解)-mikechen

死锁产生的前提是存在以下四个必要条件,下面详解死锁的四个必要条件@mikechen

什么是死锁

死锁是在多线程或多进程并发环境中的一种问题,指的是多个线程(或进程)因为竞争资源而陷入互相等待的状态,导致无法继续执行下去,程序停滞不前。

如下图所示,线程 A 持有对象A,线程 B 持有对象B,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

死锁的四个必要条件(图文详解)-mikechen

 

死锁的四个必要条件

1.互斥条件

互斥条件指的是在一段时间内,某个资源只能被一个线程或进程占用,其他线程或进程无法同时占用同一资源。

这意味着当一个线程持有资源时,其他线程无法同时获得相同的资源。

互斥条件的存在是因为某些资源是独占性的,即一次只能被一个线程使用。

例如:共享内存中的某一块区域、文件、数据库连接等资源都可能是独占性的。

因此,多个线程在并发访问这些资源时,可能会因为争夺资源而陷入死锁状态。

为了避免死锁,一种常见的方法是尽量减少对共享资源的独占性需求,以及采用适当的同步机制,也可以降低死锁的概率。

 

2.请求与保持条件

死锁的四个必要条件之一是“占有并等待条件”,也叫做“Hold and Wait”条件。

在死锁发生时,这个条件指的是每个进程至少占有一个资源,并且正在等待获取其他进程占有的资源,从而形成一个互相等待的循环。

在一个并发系统中,多个进程可以同时请求资源它可以出于以下两种状态之一:

  1. 已占有其他资源并等待:进程已经占有了一些资源,同时又请求额外的资源,然后等待这些额外资源的分配。在等待的过程中,如果无法获取所需的资源,进程可能会一直持有已占有的资源,不释放。
  2. 已释放资源并等待:进程在先前的执行中已经占有了一些资源,然后将这些资源释放,并请求新的资源。在等待新资源的分配时,它暂时不占有任何资源。

当多个进程同时出现这种“请求并等待”状态,而每个进程都在等待另一个进程释放它需要的资源时,就可能出现死锁。

 

3.不剥夺条件

资源只能由持有它的线程主动释放,其他线程无法抢占。

这意味着资源不能被强制性地从一个线程中剥夺,只有持有资源的线程可以选择何时释放。

具体来说,不剥夺条件在死锁的情况下如下:

  1. 线程 A 持有资源 R1。
  2. 线程 A 请求资源 R2。
  3. 资源 R2 被其他线程 B 持有,线程 A 被阻塞,等待资源 R2。
  4. 同时,线程 B 请求资源 R1。
  5. 资源 R1 被线程 A 持有,线程 B 被阻塞,等待资源 R1。

在这种情况下,由于资源不能被剥夺,线程 A 无法释放资源 R1,而线程 B 也无法释放资源 R2,从而导致了互相等待对方资源的状态,形成了死锁。

 

4.循环等待条件

多个线程形成一个循环等待资源的关系,每个线程都在等待另一个线程持有的资源,形成一个环路。

当以上四个条件同时满足时,就会导致死锁的发生。为了预防死锁,必须破坏其中至少一个必要条件。

例如:通过剥夺资源、限制资源申请、引入资源优先级等方式,可以降低死锁发生的概率。

 

如何避免死锁

避免死锁的主要方法包括以下几种:

1.资源有序分配法

确保每个线程按照一定的顺序申请资源,从而避免循环等待条件。

 

2.破坏请求与保持条件

在申请新资源之前,释放已经持有的资源,然后重新申请资源,从而避免死锁。

 

3.破坏不剥夺条件

允许线程在持有资源时,被其他线程抢占并释放资源。

破坏循环等待条件:引入资源的层次分级,线程只能从低级资源申请到高级资源,从而避免循环等待。

 

死锁的解决方案

如果死锁已经发生,可以采取以下几种解决方案:

剥夺资源:强制终止某些线程,释放其持有的资源,从而解除死锁状态。

回滚操作:撤销一些线程的操作,使得它们释放资源,然后重新执行。

等待释放:让一些线程等待一段时间,等待资源被释放后重新申请。

资源预分配:在启动时预分配所有可能需要的资源,从而避免后续发生死锁。

超时机制:为线程设置等待资源的超时时间,超时后放弃资源申请,释放已持有的资源。

 

1.资源预分配和释放

一种简单的方法是在每个进程开始之前,要求它一次性请求并占有所有需要的资源,然后在完成任务后一次性释放所有资源,这样可以避免“占有并等待”条件,但可能会导致资源浪费。

 

2.资源抢占

引入资源的抢占机制,允许系统从一个进程手中夺取资源并分配给其他等待资源的进程,这可以打破“非抢占条件”,但需要仔细考虑抢占的策略和优先级。

 

3.等待限制

限制进程能够等待的资源数量,以减少死锁的可能性。

例如:可以规定一个进程同时只能等待一定数量的资源,而不是无限制地等待。

 

4.资源有序分配

对资源进行编号,规定进程只能按照编号的顺序请求资源,从而防止循环等待,这需要对资源的请求和分配进行严格的控制。

 

5.死锁检测与恢复

在系统中实现死锁检测机制,定期检查系统状态是否出现死锁。

如果检测到死锁,可以采取措施来解除死锁,例如:终止某些进程或释放某些资源。

选择合适的解决方案取决于具体的应用场景和需求。预防死锁和解决死锁是并发编程中的重要课题,需要根据实际情况制定合理的策略。

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法