網站首頁 編程語言 正文
1.死鎖的定義
死鎖:指的是兩個或兩個以上的進程在執?過程中,由于競爭資源或者由于彼此通信?造成的?種阻塞的現象,若?外?作?,它們都將?法推進下去。
簡而言之,就是兩個進程在各自擁有鎖的前提下,又嘗試獲取對方鎖,從而導致程序一直處于阻塞狀態的情況。
(1)圖示說明:
線程1在擁有資源1的情況下,嘗試申請線程2所占有的資源2,而線程2又在擁有資源2的情況下,嘗試獲取線程1釋放的資源1,但是兩個線程此時都占有資源且不釋放,所以它們一直處于互相等待的狀態,即死鎖狀態。
【注】線程和鎖之間的關系:多對多的關系。一個線程可以擁有多把鎖,而一把鎖只能被一個線程所占有。
(2)代碼實例:
用synchronized對鎖對象進行加鎖,創建兩個線程,在線程1中占有鎖A嘗試獲取鎖B,在線程2中占有鎖B嘗試獲取鎖A,看看會產生怎樣的結果?
public static void main(String[] args) {
//創建兩個鎖對象
Object lockA = new Object();
Object lockB = new Object();
Thread t1 = new Thread(()->{
//占有鎖A
synchronized (lockA){
System.out.println("線程1:獲得鎖A");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//嘗試獲得線程2的鎖B
synchronized (lockB){
System.out.println("線程1:獲得鎖B");
}
}
});
t1.start();
Thread t2 = new Thread(()->{
//占有一把鎖B
synchronized (lockB){
System.out.println("線程2:獲得鎖B");
//休眠一秒,保證線程1有足夠的時間獲取鎖A
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//嘗試獲得線程1的鎖A
synchronized (lockA){
System.out.println("線程2:獲得鎖A");
}
}
});
t2.start();
}
代碼的執行的結果如下:
結果明顯可以看出,程序發生了死鎖,線程1和線程2都嘗試獲取對方的鎖但是沒有獲取到,程序執行了很長時間并未結束,兩個線程在互相等待對方釋放資源。
(3)查看死鎖
①jconsole.exe
打開Java文件目錄,找到/jdk/bin/jconsole.exe并打開,點擊本地進程連接。
在線程欄目下方點擊檢測死鎖按鈕,查看產生死鎖的進程。
在死鎖框內可以查看所有的死鎖線程,右邊的詳情信息中也可以清楚地看死鎖到線程的狀態:BLOCKED(阻塞狀態)。
②jvisualvm.exe
在Java/jdk/bin目錄下,找到jvisualvm.exe并打開,雙擊本地進程。
在線程選項下,有明顯的提示,檢測到死鎖。關于死鎖的具體詳情點擊線程Dump進行查看。
③jmc.exe
相比于前兩種方式,jmc.exe加載的信息會更加詳細,所以加載速度比較慢。
在Java/jdk/bin目錄下,找到jmc.exe并打開,點擊JMX控制臺。
進入控制臺,選擇本地進程,啟動JMX控制臺。
在JMX控制臺內,點擊線程,選擇死鎖檢測選項,即可看到產生死鎖的兩個線程,以及它們的狀態等信息。
2.死鎖的產生原因
(1)互斥條件:?個資源只能被?個線程占有,當這個資源被占?之后其他線程就只能等待。
(2)不可剝奪條件:當?個線程不主動釋放資源時,此資源?直被擁有線程占有。
(3)請求并持有條件:線程已經擁有了?個資源之后,有嘗試請求新的資源。
(4)環路等待條件:產?死鎖?定是發?了線程資源環形鏈。
【注】以上四個條件是產生死鎖的必要條件,若要產生死鎖,以上四個條件缺一不可。也就是說死鎖的產生不是由于上述四個條件當中的某一個因素所導致的,而是四個因素共同作用所導致的。
3.解決死鎖
解決思路:“反其道而行之”,若要解決思索問題,需打破形成死鎖的一個或多個條件即可。
【分析】
1.互斥條件:由于系統存在很多獨占資源,破壞 " 互斥使用“ 這一必要條件不太現實,是不能被認為打破的。
2. “不可剝奪“ 條件:當一個線程擁有資源后,無法強迫它進行釋放,這是系統所設定的本質特征,所以也無法進行改變。
3. “請求并持有“ 條件:不是系統特征,可以打破,進行認為控制。要求每個進程在運行前必須一次性申請它所要求的所有資源,且僅當該進程所要資源均可滿足時才給予一次性分配。
代碼實例:
/**
* 死鎖的產生
* 解決死鎖的方法一:破壞請求并持有條件
*/
public class unDeadLock {
public static void main(String[] args) {
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(()->{
synchronized (A){
System.out.println("線程1:得到鎖A");
//業務代碼
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//不去獲取會產生死鎖的鎖
// synchronized (B){
// System.out.println("線程1:得到鎖B");
// //業務代碼
// //.....
// System.out.println("線程1:釋放鎖B");
// }
System.out.println("線程1:釋放鎖A");
}
},"線程1");
t1.start();
Thread t2 = new Thread(()->{
synchronized (B){
System.out.println("線程2:得到鎖B");
//業務代碼
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized (A){
// System.out.println("線程1:得到鎖A");
// //業務代碼
// //.....
// System.out.println("線程1:釋放鎖A");
// }
System.out.println("線程1:釋放鎖B");
}
},"線程2");
t2.start();
}
}
運行結果:
4. 環路等待條件:采用資源有序分配法,把系統中所有資源編號,進程在申請資源時必須嚴格按資源編號的遞增次序進行,否則操作系統不予分配。通俗的說,就是改變進行的執行順序,使之不會產生死鎖。
改變原有的執行順序:
1.線程1得到鎖A,線程2進入阻塞狀態。
2.線程1在沒有釋放鎖A的請求下,又得到了鎖B,線程2還是在阻塞狀態。
3.線程1執行完了業務釋放了鎖B,線程2仍處于阻塞狀態。
4.線程1又釋放了鎖A,執行結束。
5.線程2得到鎖A,繼續執行。
6.線程2又得到了鎖B。
7.線程2釋放鎖B。
8.線程2釋放鎖A,執行結束。
代碼示例:
/**
* 破壞環路等待條件:使用順序鎖
*/
public class unDeadLock{
public static void main(String[] args) {
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(()->{
synchronized (A){
System.out.println("線程1:得到鎖A");
//業務代碼
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B){
System.out.println("線程1:得到鎖B");
System.out.println("線程1:釋放鎖B");
}
System.out.println("線程1:釋放鎖A");
}
},"線程1");
t1.start();
Thread t2 = new Thread(()->{
synchronized (A){
System.out.println("線程2:得到鎖A");
//業務代碼
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B){
System.out.println("線程1:得到鎖B");
System.out.println("線程1:釋放鎖B");
}
System.out.println("線程1:釋放鎖A");
}
},"線程2");
t2.start();
}
}
運行結果:
原文鏈接:https://blog.csdn.net/m0_52478073/article/details/124334954
相關推薦
- 2022-09-14 關于Python文本生成的Beam?Search解碼問題_python
- 2023-07-27 express 請求方式(常用) / 不完整請求路徑
- 2022-05-25 Docker?compose配置文件寫法及命令使用示例_docker
- 2022-10-15 conda虛擬環境使用pip下載包到當前環境的兩種方法_python
- 2022-01-05 console.log打印object值不對
- 2022-07-07 淺談Qt實現HTTP的Get/Post請求_C 語言
- 2023-06-02 Hadoop部署的基礎設施操作詳解_服務器其它
- 2022-08-01 C#串口編程System.IO.Ports.SerialPort類_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支