溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何檢測(cè)并避免 Java 中的死鎖

發(fā)布時(shí)間:2021-11-20 14:14:52 來(lái)源:億速云 閱讀:120 作者:柒染 欄目:大數(shù)據(jù)

今天就跟大家聊聊有關(guān)如何檢測(cè)并避免 Java 中的死鎖,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

如果你沒(méi)有參與過(guò)多線程并發(fā) Java 應(yīng)用程序的編碼,你可能會(huì)失敗。

# 如何避免 Java 線程死鎖?


這是 Java 面試 的熱門(mén)問(wèn)題之一, 也是多線程的編程中的重口味之一, 主要在招高級(jí)程序員時(shí)容易被問(wèn)到, 且有很多后續(xù)問(wèn)題。


盡管問(wèn)題看起來(lái)非常基本, 但大多數(shù) Java 開(kāi)發(fā)人員一旦你開(kāi)始深入, 就會(huì)陷入困境。


 

“什么是死鎖?????”


當(dāng)兩個(gè)或多個(gè)線程在等待彼此釋放所需的資源(鎖定)并陷入無(wú)限等待即是死鎖。它僅在多任務(wù)或多線程的情況下發(fā)生。


 

# 如何檢測(cè) Java 中的死鎖?


雖然這可以有很多答案, 但我的版本是首先我會(huì)看看代碼, 如果我看到一個(gè)嵌套的同步塊,或從一個(gè)同步的方法調(diào)用其他同步方法, 或試圖在不同的對(duì)象上獲取鎖, 如果開(kāi)發(fā)人員不是非常小心,就很容易造成死鎖。


另一種方法是在運(yùn)行應(yīng)用程序時(shí)實(shí)際鎖定時(shí)找到它, 嘗試采取線程轉(zhuǎn)儲(chǔ),在 Linux 中,你可以通過(guò)kill -3命令執(zhí)行此操作, 關(guān)注公眾號(hào)小黃鴨編程社區(qū),回復(fù)關(guān)鍵字手冊(cè),獲取最新開(kāi)發(fā)手冊(cè),這將打印應(yīng)用程序日志文件中所有線程的狀態(tài), 并且你可以看到哪個(gè)線程被鎖定在哪個(gè)線程對(duì)象上。  


你可以使用 fastthread.io 網(wǎng)站等工具分析該線程轉(zhuǎn)儲(chǔ), 這些工具允許你上載線程轉(zhuǎn)儲(chǔ)并對(duì)其進(jìn)行分析。


另一種方法是使用 jConsole 或 VisualVM, 它將顯示哪些線程被鎖定以及哪些對(duì)象被鎖定。


如果你有興趣了解故障排除工具和分析線程轉(zhuǎn)儲(chǔ)的過(guò)程, 我建議你看看 Uriah Levy 在多元視覺(jué)(PluraIsight)上《分析 Java 線程轉(zhuǎn)儲(chǔ)》課程。旨在詳細(xì)了解 Java 線程轉(zhuǎn)儲(chǔ), 并熟悉其他流行的高級(jí)故障排除工具。


 

# 編寫(xiě)一個(gè)將導(dǎo)致死鎖的Java程序?


一旦你回答了前面的問(wèn)題,他們可能會(huì)要求你編寫(xiě)代碼,這將導(dǎo)致Java死鎖。

/**   * Java 程序通過(guò)強(qiáng)制循環(huán)等待來(lái)創(chuàng)建死鎖。 */  public class DeadLockDemo {
   /*       * 此方法請(qǐng)求兩個(gè)鎖,第一個(gè)字符串,然后整數(shù)       */       public void method1() {          synchronized (String.class) {              System.out.println("Aquired lock on String.class object");
           synchronized (Integer.class) {                  System.out.println("Aquired lock on Integer.class object");              }          }      }

   /*       * 此方法也請(qǐng)求相同的兩個(gè)鎖,但完全       * 相反的順序,即首先整數(shù),然后字符串。     * 如果一個(gè)線程持有字符串鎖,則這會(huì)產(chǎn)生潛在的死鎖       * 和其他持有整數(shù)鎖,他們等待對(duì)方,永遠(yuǎn)。     */       public void method2() {          synchronized (Integer.class) {              System.out.println("Aquired lock on Integer.class object");
           synchronized (String.class) {                  System.out.println("Aquired lock on String.class object");              }          }      }  }
 

 

如果 method1() 和 method2() 都由兩個(gè)或多個(gè)線程調(diào)用,則存在死鎖的可能性, 因?yàn)槿绻€程 1 在執(zhí)行 method1() 時(shí)在 Sting 對(duì)象上獲取鎖, 線程 2 在執(zhí)行 method2() 時(shí)在 Integer 對(duì)象上獲取鎖, 等待彼此釋放 Integer 和 String 上的鎖以繼續(xù)進(jìn)行一步, 但這永遠(yuǎn)不會(huì)發(fā)生。

如何檢測(cè)并避免 Java 中的死鎖  


此圖精確演示了我們的程序, 其中一個(gè)線程在一個(gè)對(duì)象上持有鎖, 并等待其他線程持有的其他對(duì)象鎖。  


你可以看到, Thread1 需要 Thread2 持有的 Object2 上的鎖,而 Thread2 希望獲得 Thread1 持有的 Object1 上的鎖。由于沒(méi)有線程愿意放棄, 因此存在死鎖, Java 程序被卡住。


其理念是, 你應(yīng)該知道使用常見(jiàn)并發(fā)模式的正確方法, 如果你不熟悉這些模式,那么 Jose Paumard 《應(yīng)用于并發(fā)和多線程的常見(jiàn) Java 模式》是學(xué)習(xí)的好起點(diǎn)


 
 

# 如何避免Java中的死鎖?


現(xiàn)在面試官來(lái)到最后一部分, 在我看來(lái), 最重要的部分之一; 如何修復(fù)代碼中的死鎖?或如何避免Java中的死鎖?


如果你仔細(xì)查看了上面的代碼,那么你可能已經(jīng)發(fā)現(xiàn)死鎖的真正原因不是多個(gè)線程, 而是它們請(qǐng)求鎖的方式, 如果你提供有序訪問(wèn), 則問(wèn)題將得到解決。


下面是我的修復(fù)版本,它通過(guò)避免循環(huán)等待,而避免死鎖, 而不需要搶占, 這是需要死鎖的四個(gè)條件之一。

public class DeadLockFixed {
   /**       * 兩種方法現(xiàn)在都以相同的順序請(qǐng)求鎖,首先采用整數(shù),然后是 String。     * 你也可以做反向,例如,第一個(gè)字符串,然后整數(shù),       * 只要兩種方法都請(qǐng)求鎖定,兩者都能解決問(wèn)題       * 順序一致。     */      public void method1() {          synchronized (Integer.class) {              System.out.println("Aquired lock on Integer.class object");
           synchronized (String.class) {                  System.out.println("Aquired lock on String.class object");              }          }      }
   public void method2() {          synchronized (Integer.class) {              System.out.println("Aquired lock on Integer.class object");
           synchronized (String.class) {                  System.out.println("Aquired lock on String.class object");              }          }      }  }

現(xiàn)在沒(méi)有任何死鎖,因?yàn)閮煞N方法都按相同的順序訪問(wèn) Integer 和 String 類文本上的鎖。

因此,如果線程 A 在 Integer 對(duì)象上獲取鎖, 則線程 B 不會(huì)繼續(xù), 直到線程 A 釋放 Integer 鎖, 即使線程 B 持有 String 鎖, 線程 A 也不會(huì)被阻止, 因?yàn)楝F(xiàn)在線程 B 不會(huì)期望線程 A 釋放 Integer 鎖以繼續(xù)。

看完上述內(nèi)容,你們對(duì)如何檢測(cè)并避免 Java 中的死鎖有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI