1. 程式人生 > >troubleshoot之:用control+break解決執行緒死鎖問題

troubleshoot之:用control+break解決執行緒死鎖問題

[toc] # 簡介 如果我們在程式中遇到執行緒死鎖的時候,該怎麼去解決呢? 本文將會從一個實際的例子出發,一步一步的揭開java問題解決的面紗。 # 死鎖的程式碼 寫過java多執行緒程式的人應該都知道,多執行緒中一個很重要的事情就是狀態的同步,但是在狀態同步的過程中,一不小心就有可能會導致死鎖的問題。 一個最簡單的死鎖情況就是thread1佔有資源1,然後又要去獲取資源2. 而thread2佔有資源2,又要去獲取資源1的情況。 舉個具體的例子: ~~~java public class TestDeadLock { public static Object lock1= new Object(); public static Object lock2= new Object(); public static void main(String[] args) { Runnable runnable1= ()-> { System.out.println("in lock1"); synchronized(lock1){ System.out.println("Lock1 lock obj1"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock2){ System.out.println("Lock1 lock obj2"); } } }; Runnable runnable2= ()-> { System.out.println("in lock2"); synchronized(lock2){ System.out.println("Lock2 lock obj2"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock1){ System.out.println("Lock2 lock obj1"); } } }; Thread a = new Thread(runnable1); Thread b = new Thread(runnable2); a.start(); b.start(); } } ~~~ 我們執行上面的程式碼: ~~~java in lock1 Lock1 lock obj1 in lock2 Lock2 lock obj2 ~~~ 傳送了鎖迴圈等待的情況,程式執行不下去了,傳送了死鎖。 # control+break命令 在程式碼很簡單的情況下,我們很容易就能分析出來死鎖的原因,但是如果是在一個非常龐大的線上專案的時候,分析程式碼就沒有那麼容易了。 怎麼做呢? 今天教給大家一個方法,使用control+break命令。 control+break在linux表示的是Control+backslash,而在Windows下面就是Control+Break按鈕。 當然,還有一個更加通用的就是使用: kill -QUIT pid命令。 我們用jps命令獲取到執行java程式的程序id,然後執行kill -QUIT命令。 執行完畢,我們會發現執行的java程序會輸出一些額外的日誌,這些額外的日誌就是我們找出死鎖的關鍵因素。 注意,這個kill命令並不會終止程式的執行。 輸出的內容比較多,我們一部分一部分的講解。 ## Full thread dump 日誌的第一部分就是Full thread dump,包含了JVM中的所有執行緒的狀態資訊。 我們看一下我們程式碼中的兩個關鍵執行緒資訊: ~~~java "Thread-0" #13 prio=5 os_prio=31 cpu=4.86ms elapsed=230.16s tid=0x00007fc926061800 nid=0x6403 waiting for monitor entry [0x0000700008d6a000] java.lang.Thread.State: BLOCKED (on object monitor) at com.flydean.TestDeadLock.lambda$main$0(TestDeadLock.java:21) - waiting to lock <0x0000000787e868f0> (a java.lang.Object) - locked <0x0000000787e868e0> (a java.lang.Object) at com.flydean.TestDeadLock$$Lambda$14/0x0000000800b69840.run(Unknown Source) at java.lang.Thread.run([email protected]/Thread.java:832) "Thread-1" #14 prio=5 os_prio=31 cpu=4.32ms elapsed=230.16s tid=0x00007fc924869800 nid=0x6603 waiting for monitor entry [0x0000700008e6d000] java.lang.Thread.State: BLOCKED (on object monitor) at com.flydean.TestDeadLock.lambda$main$1(TestDeadLock.java:36) - waiting to lock <0x0000000787e868e0> (a java.lang.Object) - locked <0x0000000787e868f0> (a java.lang.Object) at com.flydean.TestDeadLock$$Lambda$15/0x0000000800b69c40.run(Unknown Source) at java.lang.Thread.run([email protected]/Thread.java:832) ~~~ 上面的輸出列出了執行緒名字,執行緒的優先順序,cpu時間,是否是daemon執行緒,執行緒ID,執行緒狀態等有用的資訊。 看到上面的輸出,我們看到兩個執行緒都是處於BLOCKED狀態,都在等待object monitor。 還記得執行緒的幾個狀態嗎? 我們再來複習一下。 ![](https://img-blog.csdnimg.cn/20200704111005149.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) ## 死鎖檢測 接下來的部分就是我們最關心的死鎖檢測了。 ~~~java Found one Java-level deadlock: ============================= "Thread-0": waiting to lock monitor 0x00007fc926807e00 (object 0x0000000787e868f0, a java.lang.Object), which is held by "Thread-1" "Thread-1": waiting to lock monitor 0x00007fc926807f00 (object 0x0000000787e868e0, a java.lang.Object), which is held by "Thread-0" Java stack information for the threads listed above: =================================================== "Thread-0": at com.flydean.TestDeadLock.lambda$main$0(TestDeadLock.java:21) - waiting to lock <0x0000000787e868f0> (a java.lang.Object) - locked <0x0000000787e868e0> (a java.lang.Object) at com.flydean.TestDeadLock$$Lambda$14/0x0000000800b69840.run(Unknown Source) at java.lang.Thread.run([email protected]/Thread.java:832) "Thread-1": at com.flydean.TestDeadLock.lambda$main$1(TestDeadLock.java:36) - waiting to lock <0x0000000787e868e0> (a java.lang.Object) - locked <0x0000000787e868f0> (a java.lang.Object) at com.flydean.TestDeadLock$$Lambda$15/0x0000000800b69c40.run(Unknown Source) at java.lang.Thread.run([email protected]/Thread.java:832) Found 1 deadlock. ~~~ 上面的日誌我們可以很明顯的看出來,兩個執行緒分別獲得了對方需要的鎖,所以導致了死鎖。 同時還詳細的列出了thread stack的資訊,供我們分析。 > 如果我們添加了引數-XX:+PrintConcurrentLocks,還會輸出各個執行緒的獲得的concurrent lock資訊。 ## Heap資訊 最後一部分是Heap的統計資訊: ~~~java Heap garbage-first heap total 133120K, used 3888K [0x0000000780000000, 0x0000000800000000) region size 1024K, 4 young (4096K), 0 survivors (0K) Metaspace used 1122K, capacity 4569K, committed 4864K, reserved 1056768K class space used 108K, capacity 412K, committed 512K, reserved 1048576K ~~~ > 如果我們添加了-XX:+PrintClassHistogram命令,還可以額外的輸出class直方圖統計資訊。 # 總結 上面就是使用Control+Break命令來分析java死鎖問題的具體例子,希望大家能夠喜歡。 > 本文作者:flydean程式那些事 > > 本文連結:[http://www.flydean.com/jvm-diagnostic-control-break/](http://www.flydean.com/jvm-diagnostic-control-break/) > > 本文來源:flydean的部落格 > > 歡迎關注我的公眾號:程式那些事,更多精彩等著您!