1. 程式人生 > 其它 >什麼是死鎖?死鎖如何解決?

什麼是死鎖?死鎖如何解決?

什麼是死鎖?死鎖如何解決?

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);
            }
        }
    }
}

示意圖