「操作系统」深入理解死锁(什么是死锁?死锁形成条件?如何避免死锁?如何排查死锁?)

Posted FrozenPenguin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「操作系统」深入理解死锁(什么是死锁?死锁形成条件?如何避免死锁?如何排查死锁?)相关的知识,希望对你有一定的参考价值。

「操作系统」深入理解死锁(什么是死锁?死锁形成条件?如何避免死锁?如何排查死锁?)

参考&鸣谢

什么是线程死锁?形成条件是什么?如何避免? Java圈子

面试官:如何快速排查死锁?如何避免死锁? 阿风架构笔记

怎么避免死锁? XiaoLinCoding

文章目录

一、什么是死锁

死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

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


二、死锁形成条件

互斥条件

互斥条件是指多个线程不能同时使用同一个资源

比如下图,如果线程 A 已经持有的资源,不能再同时被线程 B 持有,如果线程 B 请求获取线程 A 已经占用的资源,那线程 B 只能等待,直到线程 A 释放了资源。

持有并等待条件

持有并等待条件是指,当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1

不可剥夺条件

不可剥夺条件是指,当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。

环路等待条件

环路等待条件指的是,在死锁发生的时候,两个线程获取资源的顺序构成了环形链

比如,线程 A 已经持有资源 2,而想请求资源 1, 线程 B 已经获取了资源 1,而想请求资源 2,这就形成资源请求等待的环形图。


三、模拟死锁、排查死锁

详细请看我之前发布过的文章

「多线程锁」手写死锁案例及排查死锁原因

Jstack命令

jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。 Jstack工具可以用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

JConsole工具

Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。


四、如何避免死锁

预防

  • 破坏互斥条件:使得多个进程可以同时访问同一个资源。例如,在内存中缓存某些资源,从而避免频繁的磁盘读写。
  • 破坏不可抢占条件:当一个进程所持有的资源被其它进程请求时,可以强制该进程放弃资源,或者等待它所持有的资源被释放后才能再次获取。
  • 破坏请求与保持条件:进程在申请新资源之前先释放它所拥有的所有资源,等待新资源的分配,再重新申请先前持有的资源。
  • 破坏循环等待条件:通过给资源编号,规定每个进程按编号的顺序请求资源,释放资源的顺序与请求的顺序相反,从而避免循环等待。
  • 合理地设置超时时间:如果一个进程不能在一定时间内获得所需的所有资源,就应该释放已经获取的资源,以免造成系统资源的浪费。

五、小结

死锁是多进程并发执行中的一种常见问题,它可以被解释为若干进程因为彼此竞争系统资源而导致陷入互相等待的一种僵局状态。为了避免死锁的发生,我们需要明确死锁发生的四个必要条件:互斥条件、不可抢占条件、请求与保持条件以及循环等待条件,并采取相应的策略和措施来破坏这些条件。

其中的一些策略和措施包括:破坏互斥条件、破坏不可抢占条件、破坏请求与保持条件、破坏循环等待条件以及合理地设置超时时间。通过综合运用这些策略和措施,我们可以有效地避免死锁的发生,从而保障系统的稳定性和可靠性。

死锁的形成以及处理

一、死锁原理

      a、根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。

二、死锁的四个必要条件:
     a、互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
     b、请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。
     c、非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。
     d、循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。

三、避免死锁

     a、按同一顺序访问对象。

     b、避免事务中的用户交互。

     c、保持事务简短并处于一个批处理中。

     d、使用较低的隔离级别。

四、查看死锁例句

     开两个查询窗口,分别执行下面两段sql

     BEGIN tran
          UPDATE a SET dd=dd+1

          WaitFor Delay ‘00:01:00‘;
          SELECT * FROM B

      ROLLBACK tran

      技术分享

     BEGIN tran 
        UPDATE B SET age=age+1 
        WaitFor Delay ‘00:01:00‘;
        SELECT * FROM A     
    ROLLBACK tran

   技术分享

   五、针对上面语句出现死锁情况的解决方法

         a、根据上面提到的按同一顺序访问对象(调换一下update跟select 执行顺序)

        BEGIN tran 
            SELECT * FROM A    --将select语句放在前,update语句放在后
            WaitFor Delay ‘00:00:30‘;
            UPDATE B SET age=age+1     
       ROLLBACK tran

       技术分享

        b、在SELECT语句加With(NoLock),加With(NoLock)可能会导致脏读。

         BEGIN tran 
             UPDATE B SET age=age+1
             WaitFor Delay ‘00:00:10‘;
             SELECT * FROM A WITH(NOLOCK)    --select语句上加上with(NOLOCK),有效的避免了死锁
         ROLLBACK tran

          技术分享

         还是示例一中的语句,只是加上了WITH(NOLOCK)   

          BEGIN tran
                UPDATE a SET dd=dd+1

                WaitFor Delay ‘00:00:10‘;
                SELECT * FROM B WITH(NOLOCK)

          ROLLBACK tran

          结果:

          技术分享

         c、在sql语句前加上SET LOCK_TIMEOUT,数据默认LOCK_TIMEOUT时间是10分钟

    BEGIN tran
      SET LOCK_TIMEOUT 3000

      UPDATE a SET dd=dd+1

      --WaitFor Delay ‘00:00:10‘;
      SELECT * FROM B

    ROLLBACK tran

          技术分享--3秒钟就会终止当前SQL的执行,不会影响后面的执行效率

 

         

 

        

 

    

以上是关于「操作系统」深入理解死锁(什么是死锁?死锁形成条件?如何避免死锁?如何排查死锁?)的主要内容,如果未能解决你的问题,请参考以下文章

死锁产生的条件以及解决方法

多线程编程之线程死锁问题

数据库死锁

MySQL 加锁和死锁解析

死锁的产生原因

计算机操作系统 死锁 -- 产生死锁的必要条件死锁的处理方法(鸵鸟策略死锁检测与死锁恢复死锁预防死锁避免)