package org.springside.modules.utils.concurrent;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 由程序触发的ThreadDump,打印到日志中.
*
* 因为ThreadDump本身会造成JVM停顿,所以加上了开关和最少间隔时间的选项(默认不限制)
*
* 因为ThreadInfo的toString()最多只会打印8层的StackTrace,所以加上了最大打印层数的选项.(默认为8)
*
* @author calvin
*/
public class ThreadDumpper {
private static final int DEFAULT_MAX_STACK_LEVEL = 8;
private static Logger logger = LoggerFactory.getLogger(ThreadDumpper.class);
private boolean enable = true; // 快速关闭该功能
private long leastIntervalMills = 0; // 每次打印ThreadDump的最小时间间隔,单位为毫秒
private int maxStackLevel = DEFAULT_MAX_STACK_LEVEL; // 打印StackTrace的最大深度
private volatile Long lastThreadDumpTime = 0L;
public ThreadDumpper() {
}
public ThreadDumpper(long leastIntervalMills, int maxStackLevel) {
this.leastIntervalMills = leastIntervalMills;
this.maxStackLevel = maxStackLevel;
}
/**
* 符合条件则打印线程栈.
*/
public void threadDumpIfNeed() {
threadDumpIfNeed(null);
}
/**
* 符合条件则打印线程栈.
*
* @param reasonMsg 发生ThreadDump的原因
*/
public void threadDumpIfNeed(String reasonMsg) {
if (!enable) {
return;
}
synchronized (this) {
if (System.currentTimeMillis() - lastThreadDumpTime < leastIntervalMills) {
return;
} else {
lastThreadDumpTime = System.currentTimeMillis();
}
}
logger.info("Thread dump by ThreadDumpper" + (reasonMsg != null ? (" for " + reasonMsg) : ""));
Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
// 两条日志间的时间间隔,是VM被thread dump堵塞的时间.
logger.info("Finish the threads snapshot");
StringBuilder sb = new StringBuilder(8192 * 20).append("\n");
for (Entry<Thread, StackTraceElement[]> entry : threads.entrySet()) {
dumpThreadInfo(entry.getKey(), entry.getValue(), sb);
}
logger.info(sb.toString());
}
/**
* 打印全部的stack,重新实现threadInfo的toString()函数,因为默认最多只打印8层的stack. 同时,不再打印lockedMonitors和lockedSynchronizers.
*/
private String dumpThreadInfo(Thread thread, StackTraceElement[] stackTrace, StringBuilder sb) {
sb.append("\"").append(thread.getName()).append("\" Id=").append(thread.getId()).append(' ')
.append(thread.getState());
sb.append('\n');
int i = 0;
for (; i < Math.min(maxStackLevel, stackTrace.length); i++) {
StackTraceElement ste = stackTrace[i];
sb.append("\tat ").append(ste.toString()).append('\n');
}
if (i < stackTrace.length) {
sb.append("\t...").append('\n');
}
sb.append('\n');
return sb.toString();
}
/**
* 快速关闭打印
*/
public void setEnable(boolean enable) {
this.enable = enable;
}
/**
* 打印ThreadDump的最小时间间隔,单位为秒,默认为0不限制.
*/
public void setLeastInterval(int leastIntervalSeconds) {
synchronized (this) {
this.leastIntervalMills = TimeUnit.SECONDS.toMillis(leastIntervalSeconds);
}
}
/**
* 打印StackTrace的最大深度, 默认为8
*/
public void setMaxStackLevel(int maxStackLevel) {
this.maxStackLevel = maxStackLevel;
}
}