package com.revolsys.logging; import java.lang.Thread.State; import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; public class DeadlockLogger implements Runnable { private static final int WAIT_TIME = 60000; private static Thread thread; public static synchronized void initialize() { if (thread == null || !thread.isAlive()) { final DeadlockLogger deadlockLogger = new DeadlockLogger(); final Thread thread = new Thread(deadlockLogger, "Deadlock-detection"); thread.setDaemon(true); thread.start(); DeadlockLogger.thread = thread; } } @Override public void run() { final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); while (true) { synchronized (this) { try { wait(WAIT_TIME); } catch (final InterruptedException e) { return; } final long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null) { final ThreadInfo[] threadInfos1 = mbean.getThreadInfo(deadlockedThreadIds, true, true); final ThreadInfo[] threadInfos2 = mbean.getThreadInfo(deadlockedThreadIds, 500); final StringBuilder message = new StringBuilder("Deadlock detected\n\n"); for (int i = 0; i < threadInfos1.length; i++) { final ThreadInfo threadInfo1 = threadInfos1[i]; final ThreadInfo threadInfo2 = threadInfos2[i]; message.append(toString(threadInfo1)); message.append(toString(threadInfo2)); } Logs.error(this, message.toString()); // Quit thread now point running if there is a deadlock return; } } } } private Object toString(final ThreadInfo threadInfo) { final String threadName = threadInfo.getThreadName(); final long threadId = threadInfo.getThreadId(); final State threadState = threadInfo.getThreadState(); final StringBuilder sb = new StringBuilder( "\"" + threadName + "\"" + " Id=" + threadId + " " + threadState); final String lockName = threadInfo.getLockName(); if (lockName != null) { sb.append(" on " + lockName); } final String lockOwnerName = threadInfo.getLockOwnerName(); if (lockOwnerName != null) { final long lockOwnerId = threadInfo.getLockOwnerId(); sb.append(" owned by \"" + lockOwnerName + "\" Id=" + lockOwnerId); } if (threadInfo.isSuspended()) { sb.append(" (suspended)"); } if (threadInfo.isInNative()) { sb.append(" (in native)"); } sb.append('\n'); final StackTraceElement[] stackTrace = threadInfo.getStackTrace(); int i = 0; for (; i < stackTrace.length && i < 500; i++) { final StackTraceElement ste = stackTrace[i]; sb.append("\tat " + ste.toString()); sb.append('\n'); final LockInfo lockInfo = threadInfo.getLockInfo(); if (i == 0 && lockInfo != null) { final Thread.State ts = threadState; switch (ts) { case BLOCKED: sb.append("\t- blocked on " + lockInfo); sb.append('\n'); break; case WAITING: sb.append("\t- waiting on " + lockInfo); sb.append('\n'); break; case TIMED_WAITING: sb.append("\t- waiting on " + lockInfo); sb.append('\n'); break; default: } } final MonitorInfo[] lockedMonitors = threadInfo.getLockedMonitors(); for (final MonitorInfo mi : lockedMonitors) { if (mi.getLockedStackDepth() == i) { sb.append("\t- locked " + mi); sb.append('\n'); } } } if (i < stackTrace.length) { sb.append("\t..."); sb.append('\n'); } final LockInfo[] locks = threadInfo.getLockedSynchronizers(); if (locks.length > 0) { sb.append("\n\tNumber of locked synchronizers = " + locks.length); sb.append('\n'); for (final LockInfo li : locks) { sb.append("\t- " + li); sb.append('\n'); } } sb.append('\n'); return sb.toString(); } }