java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)

死鎖

死鎖:是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。

死鎖發生的原因

死鎖的發生是由於資源競爭導致的,導致死鎖的原因如下:

  • 系統資源不足,如果系統資源充足,死鎖出現的可能性就很低。
  • 進程(線程)運行推進的順序不合適。
  • 資源分配不當等。

死鎖發生的條件

死鎖的發生的四個必要條件:

  1. 互斥條件:一個資源每次隻能被一個進程使用。
  2. 占有且等待:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
  3. 不可強行占有:進程(線程)已獲得的資源,在未使用完之前,不能強行剝奪。
  4. 循環等待條件:若幹進程(線程)之間形成一種頭尾相接的循環等待資源關系。

這四個條件是死鎖的必要條件,隻要系統發生死鎖,這些條件必然成立,而隻要上述條件之一不滿足,就不會發生死鎖。

1:通過jstack定位死鎖信息

1.1:編寫死鎖代碼

Lock lock1 = new ReentrantLock();
  Lock lock2 = new ReentrantLock();
  
  ExecutorService exectuorService = Executors.newFixedThreadPool(2);
  
  exectuorService.submit(() -> {
     lock1.lock();
     try{
         Thread.sleep(1000);
     }catch(Exception e){}
     try{}
     finally{
       lock1.unlock();
       lock2.unlock();
     }
  });
  exectuorService.submit(() -> {
     lock2.lock();
     try{
        Thread.sleep(1000);
     }catch(Exception e){}
     
     try{}
     finally{
         lock1.unlock();
         lock2.unlock();
     }
  });

1.2:查看死鎖線程的pid

  • jps查看死鎖的線程pid
  • 使用 jstack -l pid 查看死鎖信息
  • 通過打印信息我們可以找到發生死鎖的代碼是在哪個位置
"DestroyJavaVM" #13 prio=5 os_prio=31 tid=0x00007f9a1d8fe800 nid=0xd03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
 - None

"pool-1-thread-2" #12 prio=5 os_prio=31 tid=0x00007f9a1d8fe000 nid=0xa703 waiting on condition [0x000070000ff8e000]
   java.lang.Thread.State: WAITING (parking)
 at sun.misc.Unsafe.park(Native Method)
 - parking to wait for  <0x0000000795768cd8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
 at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
 at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
 at com.coco.util.SlideTimeUnit.lambda$main$1(SlideTimeUnit.java:63)
 at com.coco.util.SlideTimeUnit$$Lambda$2/565760380.run(Unknown Source)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 at java.util.concurrent.FutureTask.run(FutureTask.java:266)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
 - <0x0000000795768d08> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
 - <0x0000000795a9e4e0> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"pool-1-thread-1" #11 prio=5 os_prio=31 tid=0x00007f9a2082c800 nid=0xa803 waiting on condition [0x000070000fe8b000]
   java.lang.Thread.State: WAITING (parking)
 at sun.misc.Unsafe.park(Native Method)
 - parking to wait for  <0x0000000795768d08> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
 at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
 at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
 at com.coco.util.SlideTimeUnit.lambda$main$0(SlideTimeUnit.java:49)
 at com.coco.util.SlideTimeUnit$$Lambda$1/596512129.run(Unknown Source)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 at java.util.concurrent.FutureTask.run(FutureTask.java:266)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
 - <0x0000000795768cd8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
 - <0x0000000795a9ba28> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007f9a2082c000 nid=0x4103 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
 - None

"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007f9a1e021800 nid=0x3f03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

2:通過Arthas工具定位死鎖

2.1: 下載好Arthas的jar,然後運行

有一個 thread -b 就可以查看到死鎖信息

[arthas@4182]$ thread -b
"pool-1-thread-2" Id=12 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@2cb8a9a3 owned by "pool-1-thread-1" Id=11
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@2cb8a9a3
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at com.coco.util.SlideTimeUnit.lambda$main$1(SlideTimeUnit.java:63)
    at com.coco.util.SlideTimeUnit$$Lambda$2/565760380.run(Unknown Source)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

    Number of locked synchronizers = 2
    - java.util.concurrent.ThreadPoolExecutor$Worker@6433a2
    - java.util.concurrent.locks.ReentrantLock$NonfairSync@3a855d13 <---- but blocks 1 other threads!

3. 通過 Jvisualvm 定位死鎖

Jvisualvm 是一種自帶的可視化工具,往往在在本地執行。

通過 Jvisualvm 命令打開軟件,選中進程,進入線程視圖,會給出死鎖提示:

死鎖的預防

  • 盡量避免使用多個鎖,並且隻有需要時才持有鎖。
  • 如果使用多個鎖,一定要設計好鎖的獲取順序。
  • 使用帶有超時的方法,為程序帶來更多的可控性,比如指定獲取鎖的時間最多為5秒,超時就放棄。
  • 通過一些代碼靜態檢查工具發現可能存在的死鎖問題,比如FindBugs。

總結

到此這篇關於java定位死鎖的文章就介紹到這瞭,更多相關java定位死鎖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: