什么是死锁?死锁如何解决?

1、死锁是什么?

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

当多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进,这种情况就是死锁。

很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。


2、死锁产生的四个必要条件

(1)互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

(2)不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。

(3)请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

(4)循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。


3、死锁的产生原因

(1)竞争资源引起进程死锁

        当系统中供多个进程共享的资源如打印机、公用队列等等,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁。

(2)可剥夺资源和不可剥夺资源

        可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺。例如,优先权高的进程可以剥夺优先权低的进程的处理机。又如,内存区可由存储器管理程序,把一个进程从一个存储区移到另一个存储区,此即剥夺了该进程原来占有的存储区,甚至可将一进程从内存调到外存上,可见,CPU和主存均属于可剥夺性资源。

        不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。

(3)竞争不可剥夺资源

        在系统中所配置的不可剥夺资源,由于它们的数量不能满足诸进程运行的需要,会使进程在运行过程中,因争夺这些资源而陷于僵局。

        例如,系统中只有一台打印机R1和一台磁带机R2,可供进程P1和P2共享。假定PI已占用了打印机R1,P2已占用了磁带机R2,若P2继续要求打印机R1,P2将阻塞;P1若又要求磁带机,P1也将阻塞。

        于是,在P1和P2之间就形成了僵局,两个进程都在等待对方释放自己所需要的资源,但是它们又都因不能继续获得自己所需要的资源而不能继续推进,从而也不能释放自己所占有的资源,以致进入死锁状态。

(4)竞争临时资源

        上面所说的打印机资源属于可顺序重复使用型资源,称为永久资源。还有一种所谓的临时资源,这是指由一个进程产生,被另一个进程使用,短时间后便无用的资源,故也称为消耗性资源,如硬件中断、信号、消息、缓冲区内的消息等,它也可能引起死锁。

        例如,SI,S2,S3是临时性资源,进程P1产生消息S1,又要求从P3接收消息S3;进程P3产生消息S3,又要求从进程P2处接收消息S2;进程P2产生消息S2,又要求从P1处接收产生的消息S1。

        如果消息通信按如下顺序进行:

P1: ···Relese(S1);Request(S3); ···

P2: ···Relese(S2);Request(S1); ···

P3: ···Relese(S3);Request(S2); ···

        并不可能发生死锁。但若改成下述的运行顺序:

P1: ···Request(S3);Relese(S1);···

P2: ···Request(S1);Relese(S2); ···

P3: ···Request(S2);Relese(S3); ···

        则可能发生死锁。


4、常用解决死锁的方法

(1)如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

(2)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率。

(3)对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。

(4)如果业务处理不好,可以用分布式事务锁或者使用乐观锁。


5、如何确保 N 个线程可以访问 N 个资源,同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。

因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。


6、编写一个将导致死锁的Java程序?

import java.util.concurrent.TimeUnit;

public class DeadLockTest {

    public static void main(String[] args) {

        String itemA = "itemA";
        String itemB = "itemB";

        // 死锁
        new Thread(new MyThread(itemA,itemB),"Thread01").start(); // 持有 A 资源,想拿 B 资源
        new Thread(new MyThread(itemB,itemA),"Thread02").start(); // 持有 B 资源,想拿 A 资源
    }
}

class MyThread implements Runnable {

    private String itemA;
    private String itemB;

    public MyThread(String itemA, String itemB) {
        this.itemA = itemA;
        this.itemB = itemB;
    }

    @Override
    public void run() {
        synchronized (itemA) {
            System.out.println(Thread.currentThread().getName() + " item: " + itemA + " => get " + itemB);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (itemB) {
                System.out.println(Thread.currentThread().getName() + " item: " + itemB + " => get " + itemA);
            }
        }
    }
}

示意图

image-20210531150526176

posted @ 2021-08-30 22:14  distance66  阅读(2668)  评论(0编辑  收藏  举报