死锁

资源总是有限的,我们在对业务进行抽象及逻辑化的过程中会不可避免的意识到这一点,运用各种各样的算法来避免我们所编写的程序因为竞争资源而陷入死锁,这非常重要。

img

定义

举个例子,甲有钥匙,乙有锁,他们都想单独完成开锁这一动作,但是各自却都不愿意把自己的东西给予对方,局面无限期地僵持下去,这就是死锁。详细说来,死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程,称为死锁进程。

产生条件

如果在计算机系统中同时具备下面四个必要条件时,那麽会发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。

  1. 互斥条件

    如独木桥就是一种独占资源,两方的人不能同时过桥。

    即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。

  2. 不可抢占条件

    如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。

    进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。

  3. 占有且申请条件

    还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。

    进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。

  4. 循环等待条件。

    就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待。

    存在一个进程等待序列{P1,P2,…,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,……,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。

解除与预防

打破互斥条件。即允许进程同时访问某些资源。但是,有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。举个粗浅例子,一双筷子,最多同时支持一个人吃饭,多了就不合理。所以,这种办法并无实用价值。

  1. 打破不可抢占条件

    如过独木桥的人强迫对方后退,或者非法地将对方推下桥,强制让桥上的人空出桥面(释放占有资源),进行过桥。

    允许进程强行从占有者那里夺取某些资源。就是说,当一个进程已占有了某些资源,它又申请新的资源,但不能立即被满足时,它必须释放所占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难,会降低系统性能。

  2. 打破占有且申请条件

    还以过独木桥为例,甲乙两人在过桥前又请来了中间人丙,丙让谁先走谁就先走,走完了,再到下一个人。

    可以实行资源预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足,则不分配任何资源,此进程暂不运行。只有当系统能够满足当前进程的全部资源需求时,才一次性地将所申请的资源全部分配给该进程。由于运行的进程已占有了它所需的全部资源,所以不会发生占有资源又申请资源的现象,因此不会发生死锁。

    但是,这种策略也有如下缺点:

    1. 在许多情况下,一个进程在执行之前不可能知道它所需要的全部资源。这是由于进程在执行时是动态的,不可预测的。
    2. 资源利用率低。无论所分资源何时用到,一个进程只有在占有所需的全部资源后才能执行。即使有些资源最后才被该进程用到一次,但该进程在生存期间却一直占有它们,造成长期占着不用的状况。这显然是一种极大的资源浪费。
    3. 降低了进程的并发性。因为资源有限,又加上存在浪费,能分配到所需全部资源的进程个数就必然少了。
  3. 打破循环等待条件

    还以过独木桥为例,谁先跨出过桥的第一步,谁就可以过桥,慢的人把脚收回去,等待下一次。

    实行资源有序分配策略。采用这种策略,即把资源事先分类编号,按号分配,使进程在申请,占用资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占用了小号资源,才能申请大号资源,就不会产生环路,从而预防了死锁。这种策略与前面的策略相比,资源的利用率和系统吞吐量都有很大提高,但是也存在以下缺点:

    1. 限制了进程对资源的请求,同时给系统中所有资源合理编号也是件困难事,并增加了系统开销。
    2. 为了遵循按编号申请的次序,暂不使用的资源也需要提前申请,从而增加了进程对资源的占用时间。

总的来说,解决死锁一般是在事前进行规避的,对死锁进行检测与恢复是十分困难的,要么重启,要么逐步撤销。而这代价又过于高昂了。

参考

  1. 死锁 – 百度百科
  2. java 死锁产生原因及解锁 – xuxiaoxie
  3. 多线程编程(二):互斥锁与死锁问题 – 术与道的分享 | w3nY@ng与您分享术与道
  4. 牛客网Java刷题知识点之什么是死锁、死锁产生的4个必要条件、死锁的解除与预防 – 大数据和人工智能躺过的坑
原文地址:https://www.cnblogs.com/Sherlock-J/p/12925967.html