Pull to refresh

Отчет о DeadLock'ах в работающем приложении

Reading time6 min
Views4.3K
Привет тебе хабражитель!
В данной статье, точнее примере я хочу показать как можно получить информацию о залоченных потоках Вашего многопоточного java приложения. Под катом будет представлен пример простейшей ситтуации DeadLock'а, а также способы получения информации о нем.

Простейшая ситтуация получения DeadLoack'а — когда один поток пытается залочить уже залоченный другим потоком ресурс.
  1. public class TestDeadLockTracker {
  2.   public static void main(String[] args) {
  3.     final Object lock1 = new Object();
  4.     final Object lock2 = new Object();
  5.     Thread t1 = new Thread("Deadlock Test T1") {
  6.       public void run() {
  7.         synchronized (lock1) {
  8.           try {
  9.             Thread.sleep(150L);
  10.           } catch (Throwable ignored) {
  11.           }
  12.           synchronized (lock2) {
  13.             try {
  14.               Thread.sleep(30L);
  15.             } catch (Throwable ignored) {
  16.             }
  17.             lock2.notify();
  18.           }
  19.           lock1.notify();
  20.         }
  21.       }
  22.     };
  23.     Thread t2 = new Thread("Deadlock Test T2") {
  24.       public void run() {
  25.         synchronized (lock2) {
  26.           try {
  27.             Thread.sleep(150L);
  28.           } catch (Throwable ignored) {
  29.           }
  30.           synchronized (lock1) {
  31.             try {
  32.               Thread.sleep(300L);
  33.             } catch (Throwable ignored) {
  34.             }
  35.             lock1.notify();
  36.           }
  37.           lock2.notify();
  38.         }
  39.       }
  40.     };
  41.     t1.start();
  42.     t2.start();
  43.   }
  44. }
* This source code was highlighted with Source Code Highlighter.

В данном примере оба потока будут залочены намертво между собой по ресурсам lock1 и lock2.
Выхода из программы не произойдет никогда. Начиная с версии java 1.5 очень просто можно отлавливать такие ситтуации, для этого существует специализированный бин для взамодействия с подсистемой потоков ThreadMXBean. Javadoc достаточно подробный так что читайте.
Давайте напишем простейший метод для получения отчета о наших проблемах с локировками.
  1. public static String reportDeadlocks(ArrayList<Long> knownDeadlocks) {
  2.   long[] threads = threadMXBean.findDeadlockedThreads();
  3.  
  4.   if ((threads == null) || (threads.length == 0)) {
  5.     return null;
  6.   }
  7.  
  8.   final ArrayList<Long> dumpIDs = new ArrayList<Long>(threads.length);
  9.   for (long thread : threads) {
  10.     boolean found = false;
  11.  
  12.     if (knownDeadlocks != null) {
  13.       Iterator itr = knownDeadlocks.iterator();
  14.       while ((!found) && (itr.hasNext())) {
  15.         found = thread == ((Number) itr.next()).longValue();
  16.       }
  17.     }
  18.  
  19.     if (found) continue;
  20.     dumpIDs.add(thread);
  21.   }
  22.  
  23.   if (dumpIDs.size() == 0) return null;
  24.  
  25.   if (knownDeadlocks != null) knownDeadlocks.addAll(dumpIDs);
  26.  
  27.   ThreadDumpFilter filter = new ThreadDumpFilter() {
  28.     public boolean include(Thread t) {
  29.       long id = t.getId();
  30.       for (Long dumpID : dumpIDs) {
  31.         if (dumpID == id) return true;
  32.       }
  33.       return false;
  34.     }
  35.  
  36.     public String toString() {
  37.       return String.format("Deadlocked threads: %s", dumpIDs);
  38.     }
  39.   };
  40.   StringWriter writer = new StringWriter();
  41.   ThreadDump.print(new PrintWriter(writer), filter);
  42.   writer.flush();
  43.  
  44.   return String.format("Found %d new deadlocked threads (%d total deadlocked):\n%s",
  45.       dumpIDs.size(), threads.length, writer.toString());
  46. }
* This source code was highlighted with Source Code Highlighter.

В переменной threadMXBean лежит инстанс бина ThreadMXBean. На второй строчке кода мы получаем список идентификаторов тредов у которых есть проблемы с локировками. После чего используем вспомогательный класс ThreadDump(есть в исходниках) для вывода информации о наших проблемных потоках. Теперь нам необходим отдельный поток для сбора информации о проблемах в нашем приложении. Код его очень простой:
  1. private static class DeadlockTrackerThread extends Thread {
  2.   private volatile boolean checking = true;
  3.  
  4.   DeadlockTrackerThread() {
  5.     super("Deadlock Tracking Thread");
  6.     setPriority(1);
  7.     setDaemon(true);
  8.   }
  9.  
  10.   public void setChecking(boolean b) {
  11.     this.checking = b;
  12.     if (this.checking) return;
  13.     interrupt();
  14.   }
  15.  
  16.   public void run() {
  17.     ArrayList<Long> knownDeadlocks = new ArrayList<Long>();
  18.     try {
  19.       while (this.checking) {
  20.         String report = ThreadUtils.reportDeadlocks(knownDeadlocks);
  21.         if (report != null) {
  22.           System.out.print("Dedlock detected " + report);
  23.         }
  24.         try {
  25.           Thread.sleep(6000L);
  26.         } catch (InterruptedException intx) {}
  27.       }
  28.     } catch (Exception e) {
  29.       System.err.print("Error: " + e.getMessage());
  30.     }
  31.   }
  32. }
* This source code was highlighted with Source Code Highlighter.

Он проверяет наличие отчета о проблемах каждые 6 секунд, и если такой присутствует то выводит его в System.out.

Теперь добавим пару строчек кода в нашу первую программу, для проверки что эта куча кода работает:
  1. public class TestDeadLockTracker {
  2.   public static void main(String[] args) {
  3.     if (!ThreadUtils.isDeadlockDetectionSupported()) {
  4.       System.out.println("DeadlockDetection not supported");
  5.       return;
  6.     }
  7.     ThreadUtils.setDeadlockDetectionEnabled(true);
  8.     ThreadUtils.setTrackingDeadlocksEnabled(true);
  9.     final Object lock1 = new Object();
  10.     ....
  11.   }
  12. }
* This source code was highlighted with Source Code Highlighter.

Запускаем и, в консоле нашей программы мы увидим примерно следующие строки:
Dedlock detected Found 2 new deadlocked threads (2 total deadlocked):
— Thread Dump Deadlocked threads: [11, 10]
Thread[Deadlock Test T1,5,main] «Deadlock Test T1» Id=10 BLOCKED on java.lang.Object@272d7a10 owned by «Deadlock Test T2» Id=11

TestDeadLockTracker$1.run(TestDeadLockTracker.java:27)
Thread[Deadlock Test T2,5,main] «Deadlock Test T2» Id=11 BLOCKED on java.lang.Object@1aa8c488 owned by «Deadlock Test T1» Id=10

TestDeadLockTracker$2.run(TestDeadLockTracker.java:45)
— End Thread Dump

Исходники
Спасибо за внимание.
Tags:
Hubs:
Total votes 32: ↑28 and ↓4+24
Comments10

Articles