package com.javamonitor.mbeans;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.javamonitor.JmxHelper;
/**
* A threading bean.
*
* @author Kees Jan Koster <kjkoster@kjkoster.org>
*/
public class Threading implements ThreadingMBean {
/**
* The object name for the threading helper mbean.
*/
public static final String objectName = JmxHelper.objectNameBase
+ "Threading";
private static final ThreadMXBean threadMXBean = ManagementFactory
.getThreadMXBean();
private static Method findDeadlockMethod = null;
static {
try {
findDeadlockMethod = ThreadMXBean.class
.getMethod("findDeadlockedThreads");
} catch (Exception ignored) {
// woops, well I guess this is a 1.5 JVM then
try {
findDeadlockMethod = ThreadMXBean.class
.getMethod("findMonitorDeadlockedThreads");
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
private ThreadInfo[] findDeadlock() throws IllegalAccessException,
InvocationTargetException {
final long[] threadIds = (long[]) findDeadlockMethod.invoke(
threadMXBean, (Object[]) null);
if (threadIds == null || threadIds.length < 1) {
// no deadlock, we're done
return null;
}
final ThreadInfo[] threads = threadMXBean.getThreadInfo(threadIds,
Integer.MAX_VALUE);
return threads;
}
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getDeadlockStacktraces()
*/
public String getDeadlockStacktraces() {
try {
final ThreadInfo[] threads = findDeadlock();
if (threads == null) {
// no deadlock, we're done
return null;
}
return stacktraces(threads, 0);
} catch (Exception e) {
return e.getMessage();
}
}
private static final int MAX_STACK = 10;
private String stacktraces(final ThreadInfo[] threads, final int i) {
if (i >= threads.length) {
return "";
}
final ThreadInfo thread = threads[i];
final StringBuilder trace = new StringBuilder();
for (int stack_i = 0; stack_i < Math.min(thread.getStackTrace().length,
MAX_STACK); stack_i++) {
if (stack_i == (MAX_STACK - 1)) {
trace.append(" ...");
} else {
trace.append(" at ").append(thread.getStackTrace()[stack_i])
.append("\n");
}
}
return "\"" + thread.getThreadName() + "\", id " + thread.getThreadId()
+ " is " + thread.getThreadState() + " on "
+ thread.getLockName() + ", owned by "
+ thread.getLockOwnerName() + ", id " + thread.getLockOwnerId()
+ "\n" + trace + "\n\n" + stacktraces(threads, i + 1);
}
/**
* We keep track of the last time we sampled the thread states. It is a
* crude optimisation to avoid having to query for the threads states versy
* often.
*/
private long lastSampled = 0L;
private final Map<Thread.State, Integer> states = new HashMap<Thread.State, Integer>();
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getThreadsBlocked()
*/
public int getThreadsBlocked() {
sampleThreads();
return states.get(Thread.State.BLOCKED);
}
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getThreadsNew()
*/
public int getThreadsNew() {
sampleThreads();
return states.get(Thread.State.NEW);
}
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getThreadsTerminated()
*/
public int getThreadsTerminated() {
sampleThreads();
return states.get(Thread.State.TERMINATED);
}
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getThreadsTimedWaiting()
*/
public int getThreadsTimedWaiting() {
sampleThreads();
return states.get(Thread.State.TIMED_WAITING);
}
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getThreadsWaiting()
*/
public int getThreadsWaiting() {
sampleThreads();
return states.get(Thread.State.WAITING);
}
/**
* @see com.javamonitor.mbeans.ThreadingMBean#getThreadsRunnable()
*/
public int getThreadsRunnable() {
sampleThreads();
return states.get(Thread.State.RUNNABLE);
}
private synchronized void sampleThreads() {
if ((lastSampled + 50L) < System.currentTimeMillis()) {
lastSampled = System.currentTimeMillis();
for (final Thread.State state : Thread.State.values()) {
states.put(state, 0);
}
for (final ThreadInfo thread : threadMXBean
.getThreadInfo(threadMXBean.getAllThreadIds())) {
if (thread != null) {
final Thread.State state = thread.getThreadState();
states.put(state, states.get(state) + 1);
} else {
states.put(Thread.State.TERMINATED, states
.get(Thread.State.TERMINATED) + 1);
}
}
}
}
}