首頁>科技>

我網上查到的死鎖

什麼是死鎖:

在申請鎖時發生了交叉閉環申請。即執行緒在獲得了鎖A並且沒有釋放的情況下去申請鎖B,這時,另一個執行緒已經獲得了鎖B,在釋放鎖B之前又要先獲得鎖A,因此閉環發生,陷入死鎖迴圈。

好,如果你能看懂理解這句話,就沒必要看下去了,你已經知道啥是死鎖了

死鎖的四個條件

1.互斥條件:程序要求對所分配的資源進行排他性控制,即在一段時間內某資源僅為一個程序所佔有。此時若有其他程序請求該資源,則請求程序只能等待。

2.請求與保持條件:程序已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他程序佔有,此時請求程序被阻塞,但對自己已獲得的資源保持不放。

3.不可剝奪條件:程序所獲得的資源在未使用完畢之前,不能被其他程序強行奪走,即只能由獲得該資源的程序自己來釋放(只能是主動釋放)

4.迴圈等待條件:存在一種程序資源的迴圈等待鏈,鏈中每一個程序已獲得的資源同時被 鏈中下一個程序所請求。即存在一個處於等待狀態的程序集合{Pl, P2, …, pn},其中Pi等 待的資源被P(i+1)佔有(i=0, 1, …, n-1),Pn等待的資源被P0佔有

好,如果你還能理解這句句話,那你真沒必要看下去了,你理解能力很強。再結合這些文章下面的一些程式碼例子。你就完全懂了

但是我沒懂,或者說當時懂了,過段時間就忘了。

我理解的死鎖

是的,就像 2個人拿槍互相指著,想要對方手上的槍

四個條件

為什麼說這就是我理解的死鎖呢?畢竟產生死鎖,需要符合4個條件,那我理解的死鎖,是否滿足呢。下面來一一對應一下。(下面的4個條件,與上文的4個大致相似,但有細微區別,我採用《Java程式設計之美》裡的描述)

tip:男人女人各為2個不同執行緒,2把搶為2個資源

《Java程式設計之美》 死鎖的4個條件

1.互斥條件:指執行緒對已經獲取到的資源進行排它性使用,即該資源只能同時只由一個執行緒佔用

一把手槍只能被一個人拿著,另外的人想要用,只能等現在正在使用的人丟了,或者從他手上搶過來。(條件1成立)

2.請求並持有條件:指一個執行緒已經持有了至少一個資源,但又提出了新的資源請求,而新的資源已經被其他執行緒佔用,所以當前執行緒會被阻塞,但阻塞的同時並不釋放自己已經獲取的資源

男的已經拿了一把槍,但是又提出了讓女的把槍給自己的要求,而這把槍已經被女的拿著了。所以男的就等著,等的時候肯定不會把自己的槍給女方。(條件2成立)

3.不可剝奪條件:指執行緒獲取到的資源在自己使用完之前不能被其他執行緒搶佔,只有在自己使用完畢後才由自己釋放資源。

2個人拿槍互指著,還能讓對方剝奪了自己的槍?也許電影裡可能有主角光環,但是兩個執行緒可沒有大魚和小魚的區別。(條件3成立)

4.環路等待條件:指發生死鎖時,必然存在一個執行緒一資源的環形鏈,即執行緒集合{T0,T1,T2..Tn}中的T0正在等待一個T1佔用的資源,T1在等T2佔用的資源,。。。Tn在等T0佔用的資源。

很明顯男的想拿女的手上的槍,女的想要拿男的手上的槍。(條件4成立)

如果你覺得2個人好像覺得條件4好像有點不好理解,那麼

(假裝明樓手上拿著槍指著明誠。。)

那,明誠想要明臺手上的槍,明臺想要明樓手上的槍,明樓想要明誠手上槍。。你看,是不是,T1->T2->T3->T1 成了個環形鏈呢。看他們站位也是個環。

紙上學來終覺淺,絕知此事要躬行

上程式碼

《Java併發程式設計之美》

class LockDemo {   //建立資源   private static Object gunA = new Object();   private static Object gunB = new Object();   public static void main(String[] agrs) {       //建立執行緒       Thread threadMan = new Thread(new Runnable() {           @Override           public void run() {               synchronized (gunA) {                   System.out.println(Thread.currentThread() + "男人拿到了槍A");                   try {                       Thread.sleep(1000);                   } catch (InterruptedException e) {                       e.printStackTrace();                   }                   System.out.println(Thread.currentThread() + "男人想要槍B");                   synchronized (gunB) {                       System.out.println(Thread.currentThread() + "男人拿到槍B");                   }               }           }       });       Thread threadWoMan = new Thread(new Runnable() {           @Override           public void run() {               synchronized (gunB) {                   System.out.println(Thread.currentThread() + "女人拿到了槍B");                   try {                       Thread.sleep(1000);                   } catch (InterruptedException e) {                       e.printStackTrace();                   }                   System.out.println(Thread.currentThread() + "女人想要槍A");                   synchronized (gunA) {                       System.out.println(Thread.currentThread() + "女人拿到了槍A");                   }               }           }       });       //啟動執行緒       threadMan.start();       threadWoMan.start();   }}

執行結果:

Thread[Thread-0,5,main]男人拿到了槍AThread[Thread-1,5,main]女人拿到了槍BThread[Thread-1,5,main]女人想要槍AThread[Thread-0,5,main]男人想要槍B

男女各自有一把槍指著對方,然後都試圖獲取對方手裡的槍,但是沒有後文。這就是一個死鎖。

程式碼分析:

程式碼先建立了2把搶,也就是2個資源,並建立了2個執行緒(看結果可以知Thread-0為男,Thread-1為女)。男的先獲取到一把槍後,執行緒sleep了1s,為的是保證女的也可以拿的槍。這樣才可以繼續後面的條件,這個時候,男的獲得了槍A,女的獲得了槍B(執行緒0獲取了資源gunA的鎖,執行緒1獲取了資源gunB的鎖)。男在拿到槍A後,檢視獲取槍B,而槍B已經被女的佔用了,女的也檢視獲取槍A。顯然也獲取不到。於是陷入等待。也就產生了死鎖。

所以\color{red}{所以}所以

2個人拿槍互相指著,想要對方手上的槍,就他媽的叫他媽的“死鎖”\color{red}{2個人拿槍互相指著,想要對方手上的槍,就他媽的叫他媽的“死鎖”}2個人拿槍互相指著,想要對方手上的槍,就他媽的叫他媽的“死鎖”

如何打破死鎖

我們來完善一下上面的場景,2個人找到了一個通往沒有bug的世界的大門,但是大門緊閉,門口放了兩把槍,只有同時拿到兩把槍,才可以進入大門(人進去後,兩把槍又回到了原來的地方)。那按照電視劇裡的劇情,是不是就會發生上面這種場面呢。一人槍一把後,互補相讓。

那麼如何才可以避免這種僵局,讓2個人都通往有bug的世界呢,其實可以一個人先拿著2把槍進去後,另一個人再去拿不就好了。

所以,打破死鎖只要打破4個條件之一就行,而4個條件能打破的只有條件2“請求並持有”和條件4“環路等待”可以打破。

造成死鎖的原因其實和上門例子裡的場景很類似,和申請資源的順序有很大原因。2個人如果有序的去拿槍進門。就避免了死鎖的產生。程式碼如何體現呢?修改一下執行緒WoMan

 Thread threadWoMan = new Thread(new Runnable() {            @Override            public void run() {                synchronized (gunA) {                    System.out.println(Thread.currentThread() + "女人拿到了搶A");                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread() + "女人想要槍B");                    synchronized (gunB) {                        System.out.println(Thread.currentThread() + "女人拿到了槍B");                    }                }            }        });

執行結果

Thread[Thread-0,5,main]男人拿到了槍AThread[Thread-0,5,main]男人想要槍BThread[Thread-0,5,main]男人拿到了槍BThread[Thread-1,5,main]女人拿到了搶AThread[Thread-1,5,main]女人想要槍BThread[Thread-1,5,main]女人拿到了槍B

程式碼分析:

男女都去獲取槍A。假設男先獲取到槍A,則女就會被阻塞,陷入等待,而不會去獲取槍B,男的獲取到槍A後,再去獲取槍B,也順利的獲取到了,進入了沒有bug的世界。這個時候2把搶又回到了原來的地方(兩把槍資源的鎖被釋放了)。女就可以去獲取槍A再獲取槍B了。

這裡幾個小點:

1.男女兩個執行緒start後。不一定是先start的就先獲取到資源槍A2.男執行緒獲取到槍A後,女執行緒不會去獲取槍B,因為程式碼裡女執行緒需要先獲取槍A,而此時槍A被男執行緒獲取了,女執行緒就陷入阻塞3.男執行緒獲取資源槍A後,獲取資源槍B的時候,不會立即釋放槍A。這裡有個容易誤解的地方,一個執行緒只能同時獲取一個資源麼,也就是一個執行緒同時只能持有一把鎖麼,答案為:不是的。 因為鎖是針對“物件”的,鎖和物件是一一對應的,所以執行緒可以同時持有多個鎖。

作者:ZhaoYun

連結:https://juejin.cn/post/6905256962750218254

11
  • 整治雙十一購物亂象,國家再次出手!該跟這些套路說再見了
  • 格力新機撞臉HTC,網友:互相抄襲還是組裝貼牌