Java并发之死锁详解

Java并发之死锁详解

死锁

(1)概念

① 进程死锁

指两个或者两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,由于这些进程都无法继续运行,导致没有一个进程可以被唤醒,就形成了进程死锁。

② 线程死锁

多个线程同时被阻塞,他们中的一个或者全部都在等待某个资源被释放,由于线程被无限期的阻塞,所以程序不可能正常终止,线程就会陷入互相等待的状态,就产生了线程死锁。

(2)产生死锁的条件

  • 互斥条件:当资源被一个线程占有时,其他线程不能使用
  • 请求和保持条件:当资源请求者在请求其他的资源的同时保持对原有资源的占用
  • 不可抢占条件:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放
  • 循环等待条件:即存在一个等待的队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源,这就形成了一个等待环路

(3) 如何排查死锁

windows

我们首先使用Java的进程查看命令来查看Java进程运行的情况jps,如图

image-20230313172016289

然后使用对应的PID去排查,使用的命令为jstack,控制台会给出死锁的详细信息。

image-20230313172024733

Linux

Linux 下可以通过 top 先定位到 CPU 占用高的 Java 进程,再利用 top -Hp 进程id 来定位是哪个线程,最后再用 jstack <pid>的输出来看各个线程栈。

另外,还可以使用jconsole工具,在jdk\bin目录下。

(4)如何预防和避免死锁

(1)预防死锁:破坏死锁产生的条件即可。

  • 互斥条件(不可改变):这个无法实现,因为我们使用锁就是为了使临界资源互斥的,不但不能改变,还得加入保证
  • 破坏请求与保持条件:
    • 方法一:开始运行前一次性申请所有需要的全部资源
      • 优点:简单容易实施且安全;
      • 缺点:资源被严重浪费,严重恶化了资源的利用率。因为某项资源不足,进程无法启动,而其他已经满足的资源也不会得到利用,严重减低了资源的利用率,造成资源浪费;使进程经常发生饥饿现象
    • 方法二:允许一个进程只获得运行初期所需的资源后,便开始运行,进程运行过程中再逐步释放已分配的且使用完毕的资源,然后再请求新的所需要的资源。
      • 优点:效率比较高,提高设备的利用率,减少发生线程饥饿的现象。
  • 破坏不可抢占条件:占用部分资源的线程进一步申请资源时,如果申请不到,可以主动释放它占有的资源
    • 当一个已经保持了某些不可被抢占资源的进程,提出新的资源请求而不能得到满足时,它必须释放已经保持的所有的资源,待以后需要时重新申请。
    • 这种方法实现起来比较负责,且代价也会比较大。释放已经保存的资源可能会导致进程之间的工作失效,反复的申请和释放资源会导致进程被无限期的延迟,这不仅延长了进程的周转时间,而且也增加了系统的开销,降低了系统的吞吐量。
  • 破坏循环等待条件:依赖按照顺序来申请资源,释放资源则反序释放
    • 对系统所有资源类型进行线性排序,并赋予不同的序号。规定每个进程必须按递增的顺序请求资源。
    • 优点:资源利用率和系统吞吐量比前两种有较明显的改善。
    • 缺点:各类资源的序号必须稳定,这就限制了新类型设备的增加;可能在没有必要的情况下拒绝资源访问。

(2)避免死锁

基本思想:系统对进程发出每一个系统都能满足的资源申请进行自动检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。

  • 安全状态:指系统能够按照某种进程推进顺序为每个进程分配所需的资源,直至满足每个进程对资源的最大需求,使每个进程都能顺利的完成,此时的状态被称为安全状态。

  • 银行家算法:指在进程提出资源申请时先进行判断,资源分配之后是否会导致不安全状态,也就是产生死锁,如果会,则不分配,让线程继续阻塞等待,如果不会,则分配资源。

    按照银行家算法的思想,当进程请求资源时,系统将按照如下原则分配系统资源:

    • 当一个进程对资源的最大需求量不超过系统中的资源数时可以接纳该线程
    • 进程可以分期请求资源,请求资源的总数不能超过最大需求量
    • 系统现有的资源不能满足进程所需的资源时,对请求的资源可以请求推迟分配,但总能使进程在有限的时间里得到资源。
    • 当系统现有的资源能满足进程需要的资源时,必须测试系统现存的资源能否满足该进程尚需的最大资源数,若能满足则按当前的申请量分配资源,否则也要推迟分配。

(5)死锁的检测

为了能对系统中是否已经发生了死锁进行检测,在系统中必须保持:①保存有关资源的请求和分配信息 ②提供一种算法,利用这些信息检测系统是否已经进入死锁状态。

  • 资源分配图:由一组结点N和一组边E所组成的一个对偶G=(N,E)。把N分为两个互斥的子集,即一组进程结点p和一组资源结点R,凡是属于E中的边,分为资源请求边和资源分配边,代表进程请求一个资源或者将资源分配给某一个进程。
  • 死锁定理:如果资源分配图中没有环路,则说明系统中没有死锁;如果存在环路,则说明存在死锁。(S为死锁状态的充分条件是:当且仅当S状态的资源分配图是不可完全简化的,该充分条件被称为死锁定理)

(6)死锁的解除

  • 抢占资源:从一个或者多个进程中抢占足够多数量的资源,分配给线程死锁,以解除死锁状态。
  • 终止线程:终止系统中的一个或多个死锁进程,直至打破循环环路,是系统从死锁状态中解脱出来。