什么是死鎖?
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。
集合中的每一個進程都在等待只能由本集合中的其他進程才能引發的事件,那么該組進程是死鎖的。
舉個例子來描述,如果此時有一個線程A,按照先鎖a再獲得鎖b的的順序獲得鎖,而在此同時又有另外一個線程B,按照先鎖b再鎖a的順序獲得鎖。如下圖所示:
產生死鎖的原因?
1.競爭資源
系統中的資源可以分為兩類:一類是可剝奪資源,是指某進程在獲得這類資源后,該資源可以再被其他進程或系統剝奪,CPU和主存均屬於可剝奪性資源;另一類資源是不可剝奪資源,當系統把這類資源分配給某進程后,再不能強行收回,只能在進程用完后自行釋放,如磁帶機、打印機等。
產生死鎖中的競爭資源之一指的是競爭不可剝奪資源(例如:系統中只有一台打印機,可供進程P1使用,假定P1已占用了打印機,若P2繼續要求打印機打印將阻塞)
產生死鎖中的競爭資源另外一種資源指的是競爭臨時資源(臨時資源包括硬件中斷、信號、消息、緩沖區內的消息等),通常消息通信順序進行不當,則會產生死鎖
2.進程間推進順序非法
若P1保持了資源R1,P2保持了資源R2,系統處於不安全狀態,因為這兩個進程再向前推進,便可能發生死鎖。例如,當P1運行到P1:Request(R2)時,將因R2已被P2占用而阻塞;當P2運行到P2:Request(R1)時,也將因R1已被P1占用而阻塞,於是發生進程死鎖
產生死鎖的四個必要條件:
-
互斥條件:進程要求對所分配的資源進行排它性控制,即在一段時間內某資源僅為一進程所占用。
- 請求和保持條件:當進程因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件:進程已獲得的資源在未使用完之前,不能剝奪,只能在使用完時由自己釋放。
- 環路等待條件:在發生死鎖時,必然存在一個進程--資源的環形鏈。
如何預防死鎖?
- 資源一次性分配:一次性分配所有資源,這樣就不會再有請求了:(破壞請求條件)
- 只要有一個資源得不到分配,也不給這個進程分配其他的資源:(破壞請保持條件)
- 可剝奪資源:即當某進程獲得了部分資源,但得不到其它資源,則釋放已占有的資源(破壞不可剝奪條件)
- 資源有序分配法:系統給每類資源賦予一個編號,每一個進程按編號遞增的順序請求資源,釋放則相反(破壞環路等待條件)
死鎖檢測
1.Jstack命令
jstack是java虛擬機自帶的一種堆棧跟蹤工具。jstack用於生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。 線程出現停頓的時候通過jstack來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后台做什么事情,或者等待什么資源。 如果java程序崩潰生成core文件,jstack工具可以用來獲得core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發生問題。另外,jstack工具還可以附屬到正在運行的java程序中,看到當時運行的java程序的java stack和native stack的信息, 如果現在運行的java程序呈現hung的狀態,jstack是非常有用的。
2.JConsole工具
Jconsole是JDK自帶的監控工具,在JDK/bin目錄下可以找到。它用於連接正在運行的本地或者遠程的JVM,對運行在Java應用程序的資源消耗和性能進行監控,並畫出大量的圖表,提供強大的可視化界面。而且本身占用的服務器內存很小,甚至可以說幾乎不消耗。
其它形式的死鎖
線程池死鎖
網絡連接池死鎖
寫一個死鎖的例子
package com.javaBase.LineDistance; /** * 〈一句話功能簡述〉; * 〈死鎖〉 * * @author jxx * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class DieLock { public static void main(String[] args) { final Object a = new Object(); final Object b = new Object(); Thread threadA = new Thread(new Runnable() { public void run() { synchronized (a) { try { System.out.println("now i in threadA-locka"); Thread.sleep(1000l); synchronized (b) { System.out.println("now i in threadA-lockb"); } } catch (Exception e) { e.printStackTrace(); } } } }); Thread threadB = new Thread(new Runnable() { public void run() { synchronized (b) { try { System.out.println("now i in threadB-lockb"); Thread.sleep(1000l); synchronized (a) { System.out.println("now i in threadB-locka"); } } catch (Exception e) { e.printStackTrace(); } } } }); threadA.start(); threadB.start(); } }