關於java中死鎖的總結

来源:https://www.cnblogs.com/zhaof/archive/2018/07/10/9291290.html
-Advertisement-
Play Games

關於死鎖,估計很多程式員都碰到過,並且有時候這種情況出現之後的問題也不是非常好排查,下麵整理的就是自己對死鎖的認識,以及通過一個簡單的例子來來接死鎖的發生,自己是做python開發的,但是對於死鎖的理解一直是一種模糊的概念,也是想過這次的整理更加清晰的認識這個概念。 用來理解的例子是一個簡單的生產者 ...


關於死鎖,估計很多程式員都碰到過,並且有時候這種情況出現之後的問題也不是非常好排查,下麵整理的就是自己對死鎖的認識,以及通過一個簡單的例子來來接死鎖的發生,自己是做python開發的,但是對於死鎖的理解一直是一種模糊的概念,也是想過這次的整理更加清晰的認識這個概念。


用來理解的例子是一個簡單的生產者和消費者模型,這裡是有一個生產者,有兩個消費者,並且註意代碼中使用notify方法的代碼行

package study_java.ex11;

import java.util.LinkedList;
import java.util.List;

public class PCDemo1 {
    public static void main(String[] args){
        Pool pool = new Pool();
        Producter p1 = new Producter(pool);
        p1.setName("p1");
        Consumer c1 = new Consumer(pool);
        Consumer c2 = new Consumer(pool);
        c1.setName("c1");
        c2.setName("c2");
        p1.start();
        c1.start();
        c2.start();
    }
}

class Pool{
    private List<Integer> list = new LinkedList<Integer>();
    private int Max = 1;
    public void addLast(int n){
        String name = Thread.currentThread().getName();
        synchronized (this){
            while (list.size() >= Max){
                try{
                    System.out.println(name+".wait()");
                    this.wait();
                }
                catch (Exception e){
                    e.printStackTrace();
                }
            }
            System.out.println(name + "+" + n);
            list.add(new Integer(n));
            System.out.println(name + ".notify()");
            this.notify();   // 註意這裡是調用的是notify方法
        }
    }
    public int remove(){
        String name = Thread.currentThread().getName();
        synchronized (this){
            while (list.size() == 0){
                try{
                    System.out.println(name + ".wait()");
                    this.wait();
                }
                catch (Exception e){
                    e.printStackTrace();
                }

            }
            System.out.println(name + "-" + 0);
            int no = list.remove(0);
            System.out.println(name + ".notify()");
            this.notify();  // 註意這裡是調用的是notify方法
            return no;
        }
    }

}

// 生產者
class Producter extends Thread{
    private Pool pool;
    static int i = 1;
    public Producter(Pool pool){
        this.pool = pool;
    }
    public void run(){
        while (true){
            pool.addLast(i++);
            System.out.println("生產者生產了"+i+"號");
        }
    }

}


// 消費者
class Consumer extends Thread{
    private Pool pool;
    public Consumer(Pool pool){
        this.pool = pool;
    }
    public void run(){
        while (true){
            int no = pool.remove();
            System.out.println("消費者消費了"+no+"號");
        }
    }

}

這段代碼的運行效果是日誌,在最後程式卡主不動了:

c1.wait()
p1+1
p1.notify()
c1-0
c1.notify()
消費者消費了1號
c1.wait()
生產者生產了2號
p1+2
p1.notify()
c1-0
c1.notify()
消費者消費了2號
c1.wait()
生產者生產了3號
p1+3
p1.notify()
c1-0
c1.notify()
消費者消費了3號
c1.wait()
生產者生產了4號
p1+4
p1.notify()
c1-0
c1.notify()
消費者消費了4號
c1.wait()
生產者生產了5號
p1+5
p1.notify()
c1-0
c1.notify()
消費者消費了5號
c1.wait()
生產者生產了6號
p1+6
p1.notify()
生產者生產了7號
c1-0
c1.notify()
消費者消費了6號
c1.wait()
p1+7
p1.notify()
生產者生產了8號
p1.wait()
c2-0
c2.notify()
消費者消費了7號
c2.wait()
c1.wait()
p1+8
p1.notify()
生產者生產了9號
p1.wait()
c2-0
c2.notify()
消費者消費了8號
c2.wait()
c1.wait()

對上面的出現卡主的情況進行分析,理解為啥會卡主:

從這次的執行效果可以看出第一次是c1搶到了執行權,但是這個時候pool是空
所以c1沒有可以消費的對象,被放入到了等待隊列

接著p1搶到了執行權,生產了1個,然後p1.notify(),這個時候等待隊列里只有c1,所以c1被喚醒,c1消費了1個,然後c1.notify(), 這個時候等待隊列也沒有等待的,這個時候有被c1搶到了執行權,但是pool里沒有可以消費的內容,所以c1.wait() 進入到等待隊列

這個時候p1搶到執行權,生產了1個,然後p1.notify(),這個時候等待隊列里只有c1,所以c1被喚醒,c1也搶到了執行權,消費了1個,然後c1.notify()
同樣這個時候等待隊列里沒有等待的,c1這次又搶到了執行權,但pool里沒有可以消費的內容,所以c1.wait(),進入到等待隊列

p1 又搶到了執行權,生產1個,然後p1.notify(),這個時候等待隊列里只有c1,所以c1被喚醒,c1也搶到了執行權,消費了1個,然後c1.notify()
同樣這個時候等待隊列里沒有等待的,c1這次又搶到了執行權,但pool里沒有可以消費的內容,所以c1.wait(),進入到等待隊列

.......這種情況重覆了幾次

但是運行到下麵這段的時候問題出現了:

p1+7
p1.notify()
生產者生產了8號
p1.wait()
c2-0
c2.notify()
消費者消費了7號
c2.wait()
c1.wait()
p1+8
p1.notify()
生產者生產了9號
p1.wait()
c2-0
c2.notify()
消費者消費了8號
c2.wait()
c1.wait()

繼續進行分析,中間重覆的部分不做分析了,和前面的過程是一樣的

這個時候等待隊裡裡依然是c1 這個時候p1搶到了執行權,生產了1個,p1.notify() 這個時候等待隊列里只有c1,所以c1被喚醒,但是c1沒有搶過p1,p1自己又搶到了執行權,但是這個時候pool裡面已經有內容,所以p1沒有生產,p1.wait(),p1進入等待隊列

這個時候c2搶到了執行權,c2消費1個,c2.notify() 這個時候等待隊里是p1,p1被喚醒,但是這個時候c2搶到了執行權,但是pool沒有內容可以消費所以c2.wait() 進入等待隊列

接著c1搶到了執行權,同樣pool沒有可以消費的內容,c1.wait() 進入到等待隊列

p1這個時候搶到了執行權,p1生產了1個,接著p1.notify() 這個時候等待隊列里有c1和c2,但是只有一個會被喚醒,不管是哪個,結果沒搶過p1,p1再次拿到執行權,但是這個時候pool已經有內容,所以p1.wait() p1進入等待隊列

從下麵是c2執行,可以看出剛纔是c2被喚醒了,這個時候c2也拿到了執行權消費了1個。c2.notify() 等待隊列里這個時候有c1 和p1 但是這個時候c2 自己搶到了執行權,但是沒有可以消費的,c2.wait() c2 進入等待隊列
不巧的是剛纔搶到執行權的正好是c1,所以c1繼續wait,再次進入等待隊列

到這個時候p1 c1 c2 都進入等待隊列里,都在等待喚醒,也就出現了程勛最後卡住不動的情況

 

解決的方法有兩種:

第一種:
其實解決上面的方法也比較簡單,就是把調用notify的地方全部換成notifyAll方法

notify和notifyAll的區別是,當執行notifyAll的時候會喚醒所有等待的線程,從而避免之前的都在等待隊列等待的問題

第二種:
就是wait()的時候加上超時參數,不是像之前一直傻等,而是在超過既定的時間之後自己喚醒

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 補充: 隊列的封裝: ...
  • R語言類 R語言的類有S3類和S4類,S3類用的比較廣,創建簡單粗糙但是靈活,而S4類比較精細,具有跟C++一樣嚴格的結構。這裡我們主要講S3類。 S3類的結構 S3類內部是一個list,append某個list類名稱,就能成為該類。list裡面的內容就是我們所說的屬性. 首先創建一個list 獲得 ...
  • 一、結論 雙重校驗鎖的單例模式代碼如下: public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getSingleton() { if ( ...
  • 最近在看Robert Sedgewick 和Kevin Wayne寫的演算法(第四版) ,看到字元串部分,正好給出了Java字元串的API(部分,也是直接自己接觸的較為常用的) 覺得自己也應該好好總結一些,首先給一點簡單的,之後看到第五章部分,有關字元串的內容再補上。 表 Java 字元串API(部分 ...
  • Description 為了表彰小聯為Samuel星球的探險所做出的貢獻,小聯被邀請參加Samuel星球近距離載人探險活動。 由於Samuel星球相當遙遠,科學家們要在飛船中度過相當長的一段時間,小聯提議用撲克牌打髮長途旅行中的無聊時間。玩了幾局之後,大家覺得單純玩撲克牌對於像他們這樣的高智商人才來 ...
  • from flask import Flask app = Flask(__name__) # app.config.update(DEBUG=True)#開啟debug模式 #載入配置文件方法一 # import config # app.config.from_object(config) # ... ...
  • 1、首先從網站下載pycharm,如下圖,根據自己電腦的操作系統進行選擇,對於windows系統選擇圖中紅色圈中的區域。 2、下載完成之後如下圖: 3、直接雙擊下載好的exe文件進行安裝,安裝截圖如下: 點擊Next進入下一步: 點擊Next進入下一步: 點擊Install進行安裝: 安裝完成後出現 ...
  • 編程語言Python語法簡單,代碼可讀性高,不僅適合初學者學習,而且崗位需求大,薪資一路也是水漲船高,即使是剛畢業的應屆畢業生,薪資也在12500元每月。 因此,很多程式員很樂意去研究這門編程語言,那麼有哪些值得收藏的Python書單呢? Python入門 《“笨辦法”學Python(第3版)》 本 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...