問題:你是怎麼發現死鎖而且是如何預防、如何解決的?java
這裏給出一個我對死鎖的概念的理解:react
多個線程同時被阻塞,而且他們中的一個或多個都在等待某個被佔用資源的釋放。spring
再給出一種百度百科上的解釋,比較全面,便於交叉理解:bash
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。服務器
注:只要一個條件不知足,就不會發生死鎖,因此避免死鎖,或者是解決死鎖問題,只須要破壞其中一個必要條件便可。ide
A 線程要在持有鎖a的前提下嘗試獲取鎖b
工具
B 線程要在持有鎖b的前提下嘗試獲取鎖a
性能
A B 在完成獲取鎖的動做以前,都不會放棄自身持有的鎖,死鎖條件達成,接下來是代碼:spa
class Solution {
public static void main(String[] args) {
final Object a = new Object();
final Object b = new Object();
Thread threadA =
new Thread(
new Runnable() {
@Override
public void run() {
synchronized (a) {
System.out.println("threadA in a lock");
try {
Thread.sleep(1000);
synchronized (b) {
System.out.println("threadA in b lock");
}
} catch (InterruptedException e) {
// ..
}
}
}
});
Thread threadB =
new Thread(
new Runnable() {
@Override
public void run() {
synchronized (b) {
System.out.println("threadB in a lock");
try {
Thread.sleep(1000);
synchronized (a) {
System.out.println("threadB in b lock");
}
} catch (InterruptedException e) {
// ..
}
}
}
});
threadA.start();
threadB.start();
}
}複製代碼
threadA in a lock
命令行
threadB in a lock
產生死鎖,程序明顯停滯了,沒有剩餘的輸出。
這裏咱們可使用JDK提供的兩種檢測工具,進行簡易的死鎖檢測
jstack工具能夠用於生成Java虛擬機當前的快照,是虛擬機中每一條線程正在執行的方法堆棧的集合。
1 咱們能夠經過 jps獲取當前任務的進程號
E:\react\spring>jps
10256
20404 Launcher
8036 Jps
8188 Solution
2 能夠確認任務的進程號是8188,而後執行jstack查看當前進程的堆棧信息
Found one Java-level deadlock:
"Thread-1":
waiting to lock monitor 0x17677e84 (object 0x073f0dd0, a java.lang.Object), which is held by "Thread-0"
"Thread-0": waiting to lock monitor 0x176798c4 (object 0x073f0dd8, a java.lang.Object),
which is held by "Thread-1"
譯爲:
hread-1這個進程,正在等待一個鎖的釋放,這個monitor的地址是 0x17677e84,它正在被Thread-0這個線程持有
Thread-0這個進程,正在等待一個鎖的釋放,這個monitor的地址是 0x176798c4,它正在被Thread-1這個線程持有。
能夠很明顯的看出,確實存在死鎖。
JConsole是JDK自帶的監控工具,在jdk/bin目錄下就能夠找到,它用來鏈接正在運行的本地或遠程JVM,對運行在Java應用程序的資源消耗和性能進行監控,而且會以圖表的形式進行展現。而且本地佔用的服務器內存很小,使用起來很是方便。
1 在命令行中敲入 jconsole 選擇進程號 8188 進行鏈接
監控圖表:
2 選擇線程選項卡,點擊檢測死鎖
如圖鎖時,使用JConsole的時候,也會顯示相似Jstack的信息,發現死鎖的線程。
正如以前提到的,避免死鎖,或者說解決死鎖問題,就須要破壞死鎖發生的必要條件。
1 儘可能不要去使用鎖的嵌套,以肯定的順序獲取鎖 ,避免可能產生的循環依賴問題。
修改後的代碼:
class Solution {
public static void main(String[] args) {
final Object a = new Object();
final Object b = new Object();
Thread threadA =
new Thread(
new Runnable() {
@Override
public void run() {
synchronized (a) {
System.out.println("threadA in a lock");
}
synchronized (b) {
System.out.println("threadA in b lock");
}
}
});
Thread threadB =
new Thread(
new Runnable() {
@Override
public void run() {
synchronized (b) {
System.out.println("threadB in a lock");
}
synchronized (a) {
System.out.println("threadB in b lock");
}
}
});
threadA.start();
threadB.start();
}
}
複製代碼
執行結果:
threadA in a lock
threadA in b lock
threadB in a lock
threadB in b lock
2 超時放棄:
能夠用ReentrantLock中的 tryLock(long time,TimeUnit unit)方法來方式獲取鎖,該方法會按照固定時長去等待鎖,所以線程能夠在獲取鎖超時以後,主動釋放以前已經釋放的鎖。(內部是一個自旋鎖,不斷的嘗試獲取鎖,超時以後拋出異常)。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
class Solution {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lockA = new ReentrantLock();
ReentrantLock lockB = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
lockA.lock();
System.out.println("Thread-2 in Alock");
Thread.sleep(100);
lockB.tryLock(1,TimeUnit.SECONDS);
System.out.println("Thread-2 in Block");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lockB.unlock();
lockA.lock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lockB.lock();
System.out.println("Thread-1 in Block");
Thread.sleep(100);
lockA.lock();
System.out.println("Thread-1 in Alock");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lockA.unlock();
lockB.lock();
}
}
}).start();
}
}複製代碼
執行結果:
Thread-2 in Alock
Thread-1 in Block
Thread-2 in Block
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) at Solution$1.run(Solution.java:22) at java.lang.Thread.run(Thread.java:748)
雖然Thread-0 拋出異常,可是Thread-1中的語句所有執行,並無發生死鎖,程序不會阻塞住。
複製代碼