當前位置:網站首頁>死鎖?如何定比特到死鎖?如何修複死鎖?(jps和jstack兩個工具)

死鎖?如何定比特到死鎖?如何修複死鎖?(jps和jstack兩個工具)

2022-01-27 19:36:23 程序員社區

死鎖?如何定比特到死鎖?如何修複死鎖?(jps和jstack兩個工具) , 你了解嗎? 本文就為大家帶來了一篇 死鎖?如何定比特到死鎖?如何修複死鎖?(jps和jstack兩個工具) 一起看看吧!
另外小編還收集了很多不錯的編程資源,希望對你有幫助:點擊查看
祝您生活愉快~

一、死鎖的概念

兩個或多個線程之間,由於互相持有對方需要的鎖,而永久處於阻塞的狀態。

我們來看一個容易造成死鎖場景的例子:

@Slf4jpublic class DeadLockThread implements Runnable {    private String lockA;    private String lockB;    public DeadLockThread(String threadName, String lockA, String lockB) {        this.lockA = lockA;        this.lockB = lockB;    }    @Override    public void run() {        synchronized (lockA) {            log.info("{} 獲取了{}", Thread.currentThread().getName(), lockA);            try {                Thread.sleep(1000L);                synchronized (lockB) {                    log.info("{} 獲取了{}", Thread.currentThread().getName(), lockB);                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}
    public static void main(String[] args) {        String lockA = "lockA";        String lockB = "lockB";        // 讓t1線程先獲取lockA,再獲取lockB        Thread t1 = new Thread(new DeadLockThread("Thread1", lockA, lockB));        // 讓t1線程先獲取lockB,再獲取lockA        Thread t2 = new Thread(new DeadLockThread("Thread2", lockB, lockA));        t1.start();        t2.start();    }

當我們啟動上述代碼後,幾乎每次都會進入死鎖的狀態,即程序一直的得不到結束,但是我們怎麼確定程序就是進入了死鎖狀態呢?

二、死鎖問題的定比特

2.1 使用jstack工具

jdk中提供了jps和jstack兩個工具,先jps查看進程ID,然後jstack查看异常未結束的進程信息;通過信息進一步分析代碼問題;如下是對如上例子中的進程pid進行jstack分析的結論:

Found one Java-level deadlock:============================="Thread-1":  waiting to lock monitor 0x000000001ccb11e8 (object 0x000000076b387cd8, a java.lang.String),  which is held by "Thread-0""Thread-0":  waiting to lock monitor 0x000000001ccb2f78 (object 0x000000076b387d10, a java.lang.String),  which is held by "Thread-1"Java stack information for the threads listed above:==================================================="Thread-1":        at cn.paic.zhangxun.restdemo.deadlock.DeadLockThread.run(DeadLockThread.java:26)        - waiting to lock <0x000000076b387cd8> (a java.lang.String)        - locked <0x000000076b387d10> (a java.lang.String)        at java.lang.Thread.run(Thread.java:745)"Thread-0":        at cn.paic.zhangxun.restdemo.deadlock.DeadLockThread.run(DeadLockThread.java:26)        - waiting to lock <0x000000076b387d10> (a java.lang.String)        - locked <0x000000076b387cd8> (a java.lang.String)        at java.lang.Thread.run(Thread.java:745)Found 1 deadlock.

可以看到,jstack分析結果中明確指出了發生了deadlock現象,並且指出來了thead0和thread1兩個線程發生了互相持有和等待對方需要的資源的死鎖情况。

2.2 使用標准API掃描

使用Java提供的標准管理API,ThreadMXBean,其有findDeadlockedThreads方法可以找到死鎖的地方;

    public static void main(String[] args) {        ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();        Runnable deadlockCheck = () -> {          long[] threadIds = mxBean.findDeadlockedThreads();          if(threadIds != null){              ThreadInfo[] threadInfos = mxBean.getThreadInfo(threadIds);              log.warn("發現死鎖線程!!!");              for(ThreadInfo threadInfo:threadInfos){                  log.info("死鎖線程ID是{},名稱是{}",threadInfo.getThreadId(), threadInfo.getThreadName());              }          }else{              log.info("當前未掃描到死鎖線程......");          }        };        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);        // 第一次等2秒,後續每5秒掃描一次        scheduledExecutorService.scheduleAtFixedRate(deadlockCheck, 2L, 5L, TimeUnit.SECONDS);    }

該方法並沒有檢測到上述例子中的死鎖,具體問題待以後確認。

2.3 代碼掃描和代碼評審

可以使用一些靜態代碼掃描工具,比如sonarlint、findbugs等進行代碼的靜態掃描;

同時加强代碼評審,請有並發經驗的同事一塊看下。

三、如何修複死鎖

絕大多數死鎖問題都是無法在線解决的,只能找到程序問題之後重新發布解决。

四、如何避免死鎖

首先,我們需要知道產生死鎖的四個必要條件:

  • 互斥;資源(鎖)同一時間只能被一個線程使用;
  • 占有且等待;一個線程占用資源後,等待獲取別的資源;
  • 不可搶占;線程已占有的資源自身不釋放時別的線程不能搶占;
  • 循環等待;每個線程都占用別的線程需要的資源並等待;

只要打破以上四個條件中的一個,死鎖就不會發生了:

  • 盡量避免使用多個鎖,並且只有需要時才持有鎖,用完立即釋放鎖;嵌套的synchronized或者lock會非常容易導致問題;
  • 盡量設計好獲取鎖的順序,使之不會出現相互持有和等待對方鎖的情况;
  • 使用帶超時的獲取鎖的方法;

版權聲明
本文為[程序員社區]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/01/202201271936234202.html

猜你喜歡

隨機推薦