Java死鎖問(wèn)題解決方案及示例詳解
1、簡(jiǎn)述
死鎖(Deadlock) 是指兩個(gè)或多個(gè)線程因爭(zhēng)奪資源而相互等待,導(dǎo)致所有線程都無(wú)法繼續(xù)執(zhí)行的一種狀態(tài)。
死鎖的四個(gè)必要條件:
- 互斥條件:某資源一次只能被一個(gè)線程占用。
- 占有且等待:一個(gè)線程已持有資源并等待其他線程的資源。
- 不可剝奪:資源不能被強(qiáng)行從線程中剝奪。
- 循環(huán)等待:多個(gè)線程形成一種頭尾相接的等待資源關(guān)系鏈。
只要這四個(gè)條件同時(shí)滿足,就可能產(chǎn)生死鎖。
2、死鎖示例代碼
下面是一個(gè)典型的死鎖示例:
public class DeadlockExample {
private static final Object LockA = new Object();
private static final Object LockB = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (LockA) {
System.out.println("Thread-1: locked A");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (LockB) {
System.out.println("Thread-1: locked B");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (LockB) {
System.out.println("Thread-2: locked B");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (LockA) {
System.out.println("Thread-2: locked A");
}
}
});
t1.start();
t2.start();
}
}
運(yùn)行這段程序可能會(huì)導(dǎo)致 Thread-1 等待 LockB,Thread-2 等待 LockA,從而產(chǎn)生死鎖。
3、如何檢測(cè)死鎖?
3.1 使用 jstack
當(dāng)應(yīng)用卡住時(shí),使用如下命令查看線程堆棧:
jps # 查找 Java 進(jìn)程 ID jstack <pid>
你會(huì)看到類(lèi)似:
Found one Java-level deadlock: "Thread-1": waiting to lock monitor 0x000..., which is held by "Thread-2" ...
3.2 使用 VisualVM 或 JConsole
這類(lèi)工具可以圖形化展示線程狀態(tài),識(shí)別死鎖非常直觀。
4、如何預(yù)防和解決死鎖?
4.1 統(tǒng)一資源獲取順序(推薦)
確保多個(gè)線程獲取多個(gè)鎖時(shí) 按相同順序加鎖。
public void safeMethod() {
synchronized (LockA) {
synchronized (LockB) {
// 安全操作
}
}
}
4.2 使用 tryLock() 避免無(wú)限等待
使用 ReentrantLock 的 tryLock() 方法設(shè)置獲取鎖的超時(shí)時(shí)間。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TryLockExample {
private final ReentrantLock lockA = new ReentrantLock();
private final ReentrantLock lockB = new ReentrantLock();
public void run() {
Thread t1 = new Thread(() -> {
try {
if (lockA.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread-1: locked A");
Thread.sleep(100);
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread-1: locked B");
lockB.unlock();
}
lockA.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread-2: locked B");
Thread.sleep(100);
if (lockA.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread-2: locked A");
lockA.unlock();
}
lockB.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
}
public static void main(String[] args) {
new TryLockExample().run();
}
}
4.3 使用 java.util.concurrent 包
避免使用低級(jí)的 synchronized,使用高級(jí)并發(fā)工具如 ExecutorService、Semaphore、Lock 等更可控的工具。
4.4 死鎖檢測(cè)與恢復(fù)
一些大型系統(tǒng)中,可以通過(guò)定期掃描線程狀態(tài),自動(dòng)檢測(cè)死鎖并重啟部分線程或服務(wù)。例如通過(guò)自定義 ThreadMXBean 檢測(cè)死鎖。
最佳實(shí)踐小結(jié):
| 技術(shù)手段 | 說(shuō)明 |
|---|---|
| 加鎖順序統(tǒng)一 | 所有線程按相同順序獲取資源 |
| tryLock + timeout | 嘗試加鎖失敗后避免長(zhǎng)時(shí)間阻塞 |
| lock 分解 | 將大鎖拆分成小鎖減少競(jìng)爭(zhēng) |
| 并發(fā)工具替代 synchronized | 使用 ReentrantLock、Semaphore 等 |
| 死鎖檢測(cè)工具 | 使用 jstack、VisualVM 等工具 |
5、結(jié)語(yǔ)
死鎖是多線程編程中不可忽視的問(wèn)題,但并不是無(wú)法避免。只要我們?cè)谠O(shè)計(jì)時(shí)保持鎖的有序性,并結(jié)合現(xiàn)代并發(fā)工具進(jìn)行控制,絕大多數(shù)死鎖問(wèn)題都是可以預(yù)防的。
以上就是Java死鎖問(wèn)題解決方案及示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java死鎖問(wèn)題解決的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
idea mybatis配置log4j打印sql語(yǔ)句的示例
本篇文章主要介紹了idea mybatis配置log4j打印sql語(yǔ)句的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Springcloud?feign傳日期類(lèi)型參數(shù)報(bào)錯(cuò)的解決方案
這篇文章主要介紹了Springcloud?feign傳日期類(lèi)型參數(shù)報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
springboot + mybatis-plus實(shí)現(xiàn)多表聯(lián)合查詢(xún)功能(注解方式)
這篇文章主要介紹了springboot + mybatis-plus實(shí)現(xiàn)多表聯(lián)合查詢(xún)功能,是最簡(jiǎn)單的一種注解方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Spring監(jiān)聽(tīng)器及定時(shí)任務(wù)實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Spring監(jiān)聽(tīng)器及定時(shí)任務(wù)實(shí)現(xiàn)方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Java實(shí)現(xiàn)簡(jiǎn)單圖書(shū)借閱系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單圖書(shū)借閱系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

