package com.onaro.util; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Arrays; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.onaro.commons.util.concurrent.OnaroExecutors; /** * Class that monitors the results of Java's ThreadMXBean.findMonitorDeadlockedThreads() method to detect * deadlocked threads in the Client. When a suspected deadlock is detected, a message is logged to the client * log file with the thread dumps of the locked threads. This class will also monitor the number of active threads * and log a warning if the number exceeds the threshold. * * @author jmyers */ public enum DeadlockMonitor { INSTANCE; public static final long DELAY = 15; // Delay for 15 seconds public static final int THREAD_COUNT_THRESHOLD = 200; // Warn if there are more than this many threads active in the client private static final Logger logger = LogManager.getLogger(DeadlockMonitor.class); private ScheduledFuture pollFuture = null; private long[] deadlockThreadIds; private int lastThreadCount = 0; public boolean isMonitoring() { return (INSTANCE.pollFuture != null && !INSTANCE.pollFuture.isCancelled()); } public static void startMonitoring() { INSTANCE.startMonitoringImpl(); } private void startMonitoringImpl() { // Only start if the not currently running if (!isMonitoring()) { pollFuture = OnaroExecutors.scheduleWithFixedDelay(pollRunnable, 0, DELAY, TimeUnit.SECONDS); } } public static void stopMonitoring() { INSTANCE.stopMonitoringImpl(); } private void stopMonitoringImpl() { // Only stop if currently running if (isMonitoring()) { pollFuture.cancel(false); pollFuture = null; } } private Runnable pollRunnable = new Runnable() { public void run() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] deadlocks = threadMXBean.findDeadlockedThreads(); if (deadlocks != null && deadlocks.length > 0 && !Arrays.equals(deadlocks, deadlockThreadIds)) { // found a new deadlock ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlocks, true, true); StringBuilder sb = new StringBuilder("****************************\nPotential thread deadlock detected - dumping blocked threads:\n"); //$NON-NLS-1$ for (ThreadInfo threadInfo : threadInfos) { sb.append(threadInfo.toString()); sb.append('\n'); StackTraceElement[] stackTraces = threadInfo.getStackTrace(); for (StackTraceElement stackTrace : stackTraces) { sb.append('\t'); sb.append(stackTrace.toString()); sb.append('\n'); } } sb.append("****************************\n"); //$NON-NLS-1$ logger.error(sb.toString()); } deadlockThreadIds = deadlocks; int threadCount = threadMXBean.getThreadCount(); if (threadCount > lastThreadCount && threadCount >= THREAD_COUNT_THRESHOLD) { logger.warn("Large number of threads active: " + threadCount); //$NON-NLS-1$ lastThreadCount = threadCount; } } }; }