最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解如何排查定位Java中的死鎖

 更新時間:2025年02月27日 10:52:39   作者:編程巫師  
在當今數(shù)字化時代,微服務架構憑借其高可擴展性、靈活性和易于維護等優(yōu)勢,成為了眾多企業(yè)構建大型應用系統(tǒng)的首選架構模式,當我們將微服務部署在 Linux 服務器上時,有時會遭遇令人頭疼的死鎖問題,本位給大家介紹了如何排查定位Java中的死鎖,需要的朋友可以參考下

一、服務死鎖,Linux 遇難題

在當今數(shù)字化時代,微服務架構憑借其高可擴展性、靈活性和易于維護等優(yōu)勢,成為了眾多企業(yè)構建大型應用系統(tǒng)的首選架構模式。當我們將微服務部署在 Linux 服務器上時,有時會遭遇令人頭疼的死鎖問題。死鎖一旦發(fā)生,就如同給微服務的運行按下了 “暫停鍵”,會導致服務無法正常響應,嚴重影響系統(tǒng)的可用性和穩(wěn)定性,進而對業(yè)務造成不良影響。

例如,在一個電商系統(tǒng)中,訂單微服務和庫存微服務可能會同時訪問共享的數(shù)據庫資源。如果訂單微服務在處理訂單時先獲取了訂單表的鎖,然后試圖獲取庫存表的鎖來更新庫存;而庫存微服務在處理庫存調整時先獲取了庫存表的鎖,接著又試圖獲取訂單表的鎖來關聯(lián)訂單信息。當這兩個操作并發(fā)執(zhí)行時,就有可能出現(xiàn)死鎖,導致訂單無法創(chuàng)建,庫存也無法更新,用戶在下單時會一直等待,嚴重影響購物體驗。

面對這樣的困境,快速準確地排查和解決死鎖問題顯得尤為重要。而 JPS(Java Virtual Machine Process Status Tool)和 Jstack(Java Stack Trace)命令,就像是兩把鋒利的 “寶劍”,為我們在 Linux 環(huán)境下排查微服務死鎖提供了有力的支持 。接下來,就讓我們深入了解如何使用這兩個命令來排查死鎖問題。

二、死鎖揭秘:原因與場景剖析

(一)死鎖形成原因

  • 系統(tǒng)資源不足:系統(tǒng)中的資源是有限的,當多個線程或進程競爭這些有限的資源時,如果資源的數(shù)量無法滿足所有線程或進程的需求,就可能導致死鎖。例如,在一個多線程的數(shù)據庫應用中,多個線程同時請求數(shù)據庫連接資源,如果數(shù)據庫連接池中的連接數(shù)量有限,當所有連接都被占用時,新的線程請求連接就會被阻塞,若這些線程在等待連接的同時又持有其他資源不釋放,就有可能引發(fā)死鎖。

  • 進程推進順序不當:進程在運行過程中,請求和釋放資源的順序不合理,也會導致死鎖的發(fā)生。比如線程 A 先獲取了資源 X,然后嘗試獲取資源 Y;而線程 B 先獲取了資源 Y,接著嘗試獲取資源 X。如果這兩個線程并發(fā)執(zhí)行,就會出現(xiàn)相互等待的情況,從而產生死鎖。

  • 資源分配不當:資源分配算法不合理或者資源分配過程中出現(xiàn)錯誤,也可能引發(fā)死鎖。例如,在一個分布式系統(tǒng)中,不同節(jié)點上的進程對共享資源的分配沒有進行有效的協(xié)調,導致某些進程獲取了過多的資源,而其他進程卻無法獲取到必要的資源,進而引發(fā)死鎖。

(二)死鎖產生的必要條件

  1. 互斥條件:指資源在某一時刻只能被一個線程或進程所使用,其他線程或進程若要使用該資源,必須等待其被釋放。例如,打印機在打印任務時,同一時間只能為一個進程服務,其他進程需要等待打印機完成當前任務后才能使用。

  2. 請求和保持條件:一個線程或進程在請求新資源的同時,會保持對已獲得資源的占有。例如,線程 A 已經獲取了資源 X,在請求資源 Y 時,它不會釋放資源 X,若資源 Y 被其他線程占用,線程 A 就會處于阻塞狀態(tài),但依然持有資源 X。

  3. 不剝奪條件:線程或進程已獲得的資源,在未使用完之前,不能被其他線程或進程強行剝奪,只能由持有該資源的線程或進程自行釋放。比如,某個線程獲得了一個文件的寫鎖,在它完成寫操作并釋放鎖之前,其他線程無法強行獲取該寫鎖。

  4. 環(huán)路等待條件:多個線程或進程之間形成一種頭尾相接的循環(huán)等待資源關系。例如,線程 A 等待線程 B 釋放資源 Y,線程 B 等待線程 C 釋放資源 Z,而線程 C 又等待線程 A 釋放資源 X,這樣就形成了一個循環(huán)等待的環(huán)路,導致死鎖。

(三)死鎖出現(xiàn)的場景

  1. 多個線程彼此申請對方資源:這是最常見的死鎖場景之一。假設有兩個線程 T1 和 T2,T1 持有資源 R1,然后試圖獲取 T2 持有的資源 R2;同時,T2 持有資源 R2,并試圖獲取 T1 持有的資源 R1。由于雙方都在等待對方釋放自己所需的資源,從而陷入死鎖。例如,在一個圖形繪制程序中,線程 T1 負責繪制圖形的輪廓,持有畫筆資源 R1,在繪制填充顏色時需要獲取顏料資源 R2;而線程 T2 負責填充顏色,持有顏料資源 R2,在繪制輪廓時需要獲取畫筆資源 R1,若它們同時執(zhí)行,就可能出現(xiàn)死鎖。

  2. 單個線程申請新資源時產生死鎖:當一個線程已經持有一些資源,在申請新的資源時,如果新資源被其他線程占用,而該線程又不釋放已持有的資源,就可能導致死鎖。比如,一個線程在處理事務時,已經獲取了數(shù)據庫的部分鎖,在需要獲取更多鎖來完成事務時,由于其他線程持有這些鎖,該線程就會陷入等待,同時它又不釋放已持有的鎖,從而引發(fā)死鎖。

三、工具登場:JPS 與 Jstack 介紹

(一)JPS 命令詳解

JPS 命令是 Java Development Kit(JDK)提供的一個工具,主要用途是列出 JVM 進程(Java 虛擬機進程)的信息。在排查服務死鎖問題時,它是我們獲取目標 Java 進程 ID 的重要手段 。在開發(fā)和調試 Java 應用程序時,使用 JPS 命令可以顯示正在運行的 Java 程序的進程 ID(PID)以及其他相關信息,如程序的完整類名,即 Java 主類類名。

JPS 命令的基本語法為:jps [ options ] [ hostid ] ,其中 option 參數(shù)用于指定不同的選項,hostid 參數(shù)用于指定要查詢的遠程主機。如果不指定任何選項,直接執(zhí)行 jps 命令,它會列出當前系統(tǒng)中所有的 Java 進程 ID 以及對應的主類名。例如,在 Linux 系統(tǒng)中打開終端,進入到項目所在目錄,執(zhí)行 jps 命令,可能會得到如下輸出:

12345 MainClass
12346 Jps

上述輸出中,12345 是運行 MainClass 的 Java 進程 ID,12346 是當前執(zhí)行 jps 命令的進程 ID。

常用的選項有:

-l:顯示完整的包名和應用程序主類名。比如執(zhí)行 jps -l ,輸出可能為:

12345 com.example.demo.MainClass
12346 sun.tools.jps.Jps

這樣我們就能更清晰地看到 Java 進程對應的完整類名。

-m:顯示完整的包名、應用程序主類名和虛擬機的啟動參數(shù)。執(zhí)行 jps -m ,輸出示例如下:

12345 com.example.demo.MainClass --param1 value1 --param2 value2
12346 sun.tools.jps.Jps -Dapplication.home=/usr/local/jdk1.8.0_291 -Xms8m

通過這個選項,我們可以了解 Java 進程啟動時傳入的參數(shù)。

-v:顯示虛擬機的啟動參數(shù)和 JVM 命令行選項。執(zhí)行 jps -v ,輸出可能是:

12345 com.example.demo.MainClass -Xmx512m -Xms256m -XX:MaxPermSize=256m
12346 sun.tools.jps.Jps -Dapplication.home=/usr/local/jdk1.8.0_291 -Xms8m

這有助于我們查看 Java 進程的 JVM 配置參數(shù)。

-q:只顯示進程 ID,不顯示類名和主類名。執(zhí)行 jps -q ,輸出結果類似:

12345
12346

這種方式在只需要獲取進程 ID 時非常簡潔高效。

(二)Jstack 命令詳解

Jstack 是 Java 虛擬機自帶的一種堆棧跟蹤工具,它的主要用途是生成 Java 虛擬機當前時刻的線程快照。線程快照是當前 Java 虛擬機內每一條線程正在執(zhí)行的方法堆棧的集合,通過分析這個快照,我們可以定位線程出現(xiàn)長時間停頓的原因,比如線程間死鎖、死循環(huán)、請求外部資源導致的長時間等待等。在排查微服務死鎖問題時,Jstack 命令起著關鍵作用,它能夠幫助我們深入了解線程的運行狀態(tài)和方法調用情況,從而找出死鎖的根源。

Jstack 命令的基本語法為:jstack [ options ] pid ,其中 options 是可選參數(shù),pid 是要分析的 Java 進程 ID。常用的選項有:

  • -l:Long listing,會打印出額外的鎖信息,在發(fā)生死鎖時可以用 jstack -l pid 來觀察鎖持有情況。當我們懷疑微服務出現(xiàn)死鎖時,使用這個選項可以獲取更詳細的鎖相關信息,例如:
jstack -l 12345

執(zhí)行上述命令后,輸出結果中會包含每個線程持有的鎖以及等待獲取的鎖的詳細信息,這對于判斷是否存在死鎖以及死鎖的具體情況非常有幫助。

  • -m:mixed mode,不僅會輸出 Java 堆棧信息,還會輸出 C/C++ 堆棧信息(比如 Native 方法)。如果 Java 程序中調用了本地方法,使用這個選項可以查看本地方法的堆棧信息,有助于全面分析問題,命令示例如下:
jstack -m 12345
  • -F:Force a stack dump when jstack [-l] pid does not respond。當正常的請求不被響應時,強制輸出堆棧信息。在某些情況下,目標 Java 進程可能處于無響應狀態(tài),此時使用 -F 選項可以強制獲取線程堆棧信息,例如:
jstack -F 12345

通過 Jstack 命令獲取到的線程堆棧信息中,包含了豐富的內容,如線程的狀態(tài)(RUNNABLE、BLOCKED、WAITING 等)、線程正在執(zhí)行的方法、方法的調用棧以及鎖的持有和等待情況等。這些信息對于我們排查死鎖問題至關重要,能夠幫助我們準確地定位到死鎖發(fā)生的位置和原因。

四、實戰(zhàn)演練:排查死鎖步驟

(一)復現(xiàn)死鎖:Java 代碼示例

下面是一段導致死鎖的 Java 代碼示例,通過這段代碼可以清晰地看到線程是如何競爭資源并最終導致死鎖的。

public class DeadLockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock1");
                try {
                    Thread.sleep(1000); // 讓線程1持有l(wèi)ock1一段時間,確保線程2有機會獲取lock2
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1: Waiting for lock2");
                synchronized (lock2) {
                    System.out.println("Thread 1: Holding lock1 and lock2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock2");
                try {
                    Thread.sleep(1000); // 讓線程2持有l(wèi)ock2一段時間,確保線程1有機會獲取lock1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 2: Waiting for lock1");
                synchronized (lock1) {
                    System.out.println("Thread 2: Holding lock1 and lock2");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

在這段代碼中,thread1 首先獲取了 lock1 ,然后睡眠 1 秒,這期間 thread2 有機會獲取 lock2 。接著,thread1 試圖獲取 lock2 ,而 thread2 試圖獲取 lock1 ,由于雙方都持有對方需要的資源且不釋放,從而形成了死鎖。

(二)使用 JPS 查找進程 ID

將上述 Java 代碼打包成一個可執(zhí)行的 JAR 文件,然后部署到 Linux 服務器上運行。在 Linux 終端中,使用 jps 命令來查找正在運行的 Java 進程 ID。假設我們將這個 JAR 文件命名為 deadlock-demo.jar ,運行命令如下:

java -jar deadlock-demo.jar

運行后,打開新的終端,執(zhí)行 jps 命令:

jps

輸出結果可能如下:

12345 DeadLockExample
12346 Jps

這里的 12345 就是運行 DeadLockExample 類的 Java 進程 ID,我們后續(xù)排查死鎖就需要用到這個 ID。

(三)使用 Jstack 分析線程堆棧

得到 Java 進程 ID 后,使用 jstack 命令來分析線程堆棧信息,從而定位死鎖。執(zhí)行命令如下:

jstack -l 12345

其中,-l 選項表示輸出額外的鎖信息,這對于分析死鎖非常有幫助。命令執(zhí)行后,會輸出大量的線程堆棧信息,我們重點關注與死鎖相關的部分。以下是可能的輸出結果(為了突出重點,進行了簡化):

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f85a8003ae8 (object 0x00000007d6aa2c98, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f85a8006168 (object 0x00000007d6aa2ca8, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at DeadLockExample.lambda$main$1(DeadLockExample.java:22)
        - waiting to lock <0x00000007d6aa2c98> (a java.lang.Object)
        - locked <0x00000007d6aa2ca8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

"Thread-0":
        at DeadLockExample.lambda$main$0(DeadLockExample.java:12)
        - waiting to lock <0x00000007d6aa2ca8> (a java.lang.Object)
        - locked <0x00000007d6aa2c98> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

從輸出結果中可以看到,Thread-1 正在等待獲取 Thread-0 持有的鎖(0x00000007d6aa2c98 ),而 Thread-0 正在等待獲取 Thread-1 持有的鎖(0x00000007d6aa2ca8 ),這就形成了死鎖。同時,還可以看到死鎖發(fā)生的具體代碼行,如 DeadLockExample.java:22 和 DeadLockExample.java:12 ,這為我們進一步排查和解決死鎖問題提供了關鍵線索。

五、總結與展望

在本次死鎖排查過程中,我們首先通過一個簡單的 Java 代碼示例復現(xiàn)了死鎖問題,然后借助 JPS 命令快速準確地獲取到了目標 Java 進程 ID,為后續(xù)的分析工作奠定了基礎。接著,使用 Jstack 命令對線程堆棧進行分析,成功找到了死鎖的關鍵信息,包括死鎖的線程、持有的鎖以及等待獲取的鎖等,從而清晰地定位到了死鎖的根源。

死鎖問題對系統(tǒng)的正常運行危害極大,它不僅會導致服務中斷,影響用戶體驗,還可能造成資源的浪費和系統(tǒng)性能的下降。因此,在開發(fā)和部署服務時,避免死鎖的發(fā)生至關重要。為了預防死鎖,我們可以采取多種措施。在代碼編寫階段,要確保所有線程以相同的順序獲取鎖,避免嵌套鎖的使用,減少鎖的持有時間。在資源分配方面,合理規(guī)劃資源的使用和分配,避免資源的過度競爭和不合理分配。同時,可以使用一些高級的同步工具,如 ReentrantLock、Semaphore 等,它們提供了更靈活的同步控制,有助于降低死鎖發(fā)生的風險。此外,定期對系統(tǒng)進行性能測試和死鎖檢測,及時發(fā)現(xiàn)并解決潛在的死鎖問題也是非常必要的。

隨著架構的不斷發(fā)展和應用場景的日益復雜,死鎖問題可能會以更加隱蔽和復雜的形式出現(xiàn)。未來,我們需要不斷探索和研究新的死鎖檢測和預防技術,結合人工智能、大數(shù)據分析等新興技術,實現(xiàn)對死鎖問題的智能預測和自動處理,進一步提升微服務系統(tǒng)的穩(wěn)定性和可靠性。

以上就是一文詳解如何排查定位Java中的死鎖的詳細內容,更多關于排查定位Java死鎖的資料請關注腳本之家其它相關文章!

相關文章

  • 程序猿必須要掌握的多線程安全問題之鎖策略詳解

    程序猿必須要掌握的多線程安全問題之鎖策略詳解

    在筆者面試過程時,經常會被問到各種各樣的鎖,如樂觀鎖、讀寫鎖等等,非常繁多,在此做一個總結,介紹的內容如下,需要的朋友可以參考下
    2021-06-06
  • Mybatis generator自動生成代碼插件實例解析

    Mybatis generator自動生成代碼插件實例解析

    這篇文章主要介紹了Mybatis generator自動生成代碼插件實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • 遞歸出現(xiàn)棧溢出stackoverflow的問題及解決

    遞歸出現(xiàn)棧溢出stackoverflow的問題及解決

    這篇文章主要介紹了關于遞歸出現(xiàn)棧溢出stackoverflow的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • JavaCV調用百度AI實現(xiàn)人臉檢測方法詳解

    JavaCV調用百度AI實現(xiàn)人臉檢測方法詳解

    在檢測人臉數(shù)量、位置、性別、口罩等場景時,可以考慮使用百度開放平臺提供的web接口,一個web請求就能完成檢測得到結果。本文就為大家介紹JavaCV如何調用百度AI實現(xiàn)最簡單的人臉檢測,需要的可以參考一下
    2022-01-01
  • SpringBoot整合七牛云上傳圖片的示例代碼

    SpringBoot整合七牛云上傳圖片的示例代碼

    本文就來介紹了SpringBoot整合七牛云上傳圖片的示例代碼,用戶在前端上傳圖片后,交由后端處理,上傳至七牛云,感興趣的可以了解一下
    2023-10-10
  • 如何使用新方式編寫Spring MVC接口

    如何使用新方式編寫Spring MVC接口

    這篇文章主要介紹了如何使用新方式編寫Spring MVC接口,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • 詳解JAVA后端實現(xiàn)統(tǒng)一掃碼支付:微信篇

    詳解JAVA后端實現(xiàn)統(tǒng)一掃碼支付:微信篇

    本篇文章主要介紹了詳解JAVA后端實現(xiàn)統(tǒng)一掃碼支付:微信篇,這里整理了詳細的代碼,有需要的小伙伴可以參考下。
    2017-01-01
  • 關于Mybatis-Plus字段策略與數(shù)據庫自動更新時間的一些問題

    關于Mybatis-Plus字段策略與數(shù)據庫自動更新時間的一些問題

    這篇文章主要介紹了關于Mybatis-Plus字段策略與數(shù)據庫自動更新時間的一些問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • Java異常處理與throws關鍵字用法分析

    Java異常處理與throws關鍵字用法分析

    這篇文章主要介紹了Java異常處理與throws關鍵字用法,結合實例形式分析了java常見的異常、錯誤處理及throws關鍵字相關使用技巧、注意事項,需要的朋友可以參考下
    2019-01-01
  • SpringBoot小程序推送信息的項目實踐

    SpringBoot小程序推送信息的項目實踐

    本文主要介紹了SpringBoot小程序推送信息的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04

最新評論

张家川| 上虞市| 巴林右旗| 延川县| 万源市| 乌审旗| 汝南县| 临洮县| 武清区| 泽州县| 沈阳市| 镇赉县| 平顶山市| 马山县| 城口县| 会同县| 息烽县| 广南县| 郁南县| 宜城市| 慈利县| 茂名市| 定陶县| 东乡县| 东乌珠穆沁旗| 台东市| 新宾| 天全县| 即墨市| 昌江| 邛崃市| 宁津县| 河曲县| 大姚县| 巢湖市| 正蓝旗| 镇雄县| 巴彦县| 拉萨市| 寿阳县| 蒙城县|