作者:Yujiaao
https://segmentfault.com/a/1190000019962661
經典但核心Java面試問題之一,
如果你沒有參與過多執行緒并發 Java 應用程式的編碼,你可能會失敗,
如何避免 Java 執行緒死鎖?
如何避免 Java 中的死鎖?
這是 Java 面試的熱門問題之一, 也是多執行緒的編程中的重口味之一, 主要在招高級程式員時容易被問到, 且有很多后續問題,
盡管問題看起來非常基本, 但大多數 Java 開發人員一旦你開始深入, 就會陷入困境,
面試問題總是以“什么是死鎖???”開始
當兩個或多個執行緒在等待彼此釋放所需的資源(鎖定)并陷入無限等待即是死鎖,它僅在多任務或多執行緒的情況下發生,
如何檢測 Java 中的死鎖?
雖然這可以有很多答案, 但我的版本是首先我會看看代碼, 如果我看到一個嵌套的同步塊,或從一個同步的方法呼叫其他同步方法, 或試圖在不同的物件上獲取鎖, 如果開發人員不是非常小心,就很容易造成死鎖,
另一種方法是在運行應用程式時實際鎖定時找到它, 嘗試采取執行緒轉儲,在 Linux 中,你可以通過kill -3命令執行此操作, 這將列印應用程式日志檔案中所有執行緒的狀態, 并且你可以看到哪個執行緒被鎖定在哪個執行緒物件上,
你可以使用 fastthread.io 網站等工具分析該執行緒轉儲, 這些工具允許你上載執行緒轉儲并對其進行分析,
另一種方法是使用 jConsole 或 VisualVM, 它將顯示哪些執行緒被鎖定以及哪些物件被鎖定,
如果你有興趣了解故障排除工具和分析執行緒轉儲的程序, 我建議你看看 Uriah Levy 在多元視覺(PluraIsight)上《分析 Java 執行緒轉儲》課程,旨在詳細了解 Java 執行緒轉儲, 并熟悉其他流行的高級故障排除工具,
撰寫一個將導致死鎖的Java程式?
一旦你回答了前面的問題,他們可能會要求你撰寫代碼,這將導致Java死鎖,
這是我的版本之一
/**
* Java 程式通過強制回圈等待來創建死鎖,
*/
public class DeadLockDemo {
/*
* 此方法請求兩個鎖,第一個字串,然后整數
*/
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");
}
}
}
/*
* 此方法也請求相同的兩個鎖,但完全
* 相反的順序,即首先整數,然后字串,
* 如果一個執行緒持有字串鎖,則這會產生潛在的死鎖
* 和其他持有整數鎖,他們等待對方,永遠,
*/
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() 都由兩個或多個執行緒呼叫,則存在死鎖的可能性, 因為如果執行緒 1 在執行 method1() 時在 Sting 物件上獲取鎖, 執行緒 2 在執行 method2() 時在 Integer 物件上獲取鎖, 等待彼此釋放 Integer 和 String 上的鎖以繼續進行一步, 但這永遠不會發生,

此圖精確演示了我們的程式, 其中一個執行緒在一個物件上持有鎖, 并等待其他執行緒持有的其他物件鎖,
你可以看到, Thread1 需要 Thread2 持有的 Object2 上的鎖,而 Thread2 希望獲得 Thread1 持有的 Object1 上的鎖,由于沒有執行緒愿意放棄, 因此存在死鎖, Java 程式被卡住,
其理念是, 你應該知道使用常見并發模式的正確方法, 如果你不熟悉這些模式,那么 Jose Paumard 《應用于并發和多執行緒的常見 Java 模式》是學習的好起點,另外關注微信公眾號Java技術堆疊,也可以閱讀很多設計模式干貨,
如何避免Java中的死鎖?
現在面試官來到最后一部分, 在我看來, 最重要的部分之一; 如何修復代碼中的死鎖?或如何避免Java中的死鎖?
如果你仔細查看了上面的代碼,那么你可能已經發現死鎖的真正原因不是多個執行緒, 而是它們請求鎖的方式, 如果你提供有序訪問, 則問題將得到解決,
下面是我的修復版本,它通過避免回圈等待,而避免死鎖, 而不需要搶占, 這是需要死鎖的四個條件之一,
public class DeadLockFixed {
/**
* 兩種方法現在都以相同的順序請求鎖,首先采用整數,然后是 String,
* 你也可以做反向,例如,第一個字串,然后整數,
* 只要兩種方法都請求鎖定,兩者都能解決問題
* 順序一致,
*/
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");
}
}
}
}
現在沒有任何死鎖,因為兩種方法都按相同的順序訪問 Integer 和 String 類文本上的鎖,
因此,如果執行緒 A 在 Integer 物件上獲取鎖, 則執行緒 B 不會繼續, 直到執行緒 A 釋放 Integer 鎖, 即使執行緒 B 持有 String 鎖, 執行緒 A 也不會被阻止, 因為現在執行緒 B 不會期望執行緒 A 釋放 Integer 鎖以繼續,
近期熱文推薦:
1.Java 15 正式發布, 14 個新特性,重繪你的認知!!
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/245514.html
標籤:Java
