首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

线程运作栈信息的获取

2012-12-20 
线程运行栈信息的获取线程运行栈信息的获取 一、问题的引入 我们在Java程序中使用日志功能(JDK Log或者Log4

线程运行栈信息的获取

线程运行栈信息的获取
一、问题的引入
我们在Java程序中使用日志功能(JDK Log或者Log4J)的时候,会发现Log系统会自动帮我们打印出丰富的信息,格式一般如下:
[运行时间] [当前类名] [方法名]
INFO: [用户信息]
具体例子如Tomcat启动信息:
Jul 9, 2004 11:22:41 AM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on port 8080

看起来这毫无神奇之处,不就是打印了一条信息吗?但如果好奇心重一点,追寻后面的实现原理,会发现这确实很神奇。
上面的Log信息的[当前类名] [方法名]部分 不是用户自己添加的,而是Log系统自动添加的。这意味着Log系统能够自动判断当前执行语句是哪个类的哪个方法。这是如何做到的?
我们翻遍java.lang.reflection package,幻想着找到一个Statement语句级别的Reflection类,通过这个Statement对象获得Method,然后通过这个Method获得declared Class。这不就获得对应的Class和Method信息了吗?这是一个不错的构想,但也只能是一个构想;因为没有这个Statement对象。
再想一下。对了,Java不是有一个Thread类吗?Thread.currentThread()方法获取当前线程,我们能不能通过这个当前线程获取当前运行的Method和Class呢?很遗憾,如果你还在用JDK1.4或以下版本,那么找不到这样的方法。(JDK1.5的情况后面会讲)
再想一下。对了,我们都有很深刻的印象,当系统抛出Exception的时候,总是打印出一串的信息,告诉我们Exception发生的位置,和一层一层的调用关系。我们也可以自己调用Exception的printStackTrace()方法来打印这些信息。这不就是当前线程运行栈的信息吗?找到了,就是它。
Exception的printStackTrace()方法继承自Throwable,那么我们来看一下,JDK的Throwable的printStackTrace()方法是如何实现的。
我们先来看JDK1.3的源代码,会发现Throwable.printStackTrace()方法调用了一个native printStackTrace0()方法。我们找不到任何线索,可以用在我们自己的Java代码中。
那怎么办?Throwable.printStackTrace()的输出结果字符串里面不是包含了当前线程运行栈的所有信息吗?我们可以从这个字符串中抽取自己需要的信息。JDK1.3的时代,也只能这么做了。
二、Log4J 1.2的相关实现
Log4J 1.2是JDK1.3时代的作品。我们来看相关源代码。

?线程运作栈信息的获取while (ix < stack.length) {
??? StackTraceElement frame = stack[ix];
??? String cname = frame.getClassName();
??? if (!cname.equals("java.util.logging.Logger"))
// We've found the relevant frame.
… // 这里的代码省略
}
// We haven't found a suitable frame, so just punt.? This is
??????? // OK as we are only committed to making a "best effort" here.
??? }

从注释中就可以看出实现思路。过程和Log4J异曲同工。只是免去了解析字符串的麻烦。
四、Log4J 1.3 alpha的相关实现
既然JDK1.4中引入了StackTraceElement类,Log4J也要与时俱进。LocationInfo类也有了相应的变化。

? /**
???? Instantiate location information based on a Throwable. We
???? expect the Throwable <code>t</code>, to be in the format

?????? <pre>
??????? java.lang.Throwable
??????? ...
????????? at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
????????? at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
??????? at org.apache.log4j.Category.callAppenders(Category.java:131)
??????? at org.apache.log4j.Category.log(Category.java:512)
??????? at callers.fully.qualified.className.methodName(FileName.java:74)
??????? ...
?????? </pre>

?????? <p>However, we can also deal with JIT compilers that "lose" the
?????? location information, especially between the parentheses.

??? */
? public LocationInfo(Throwable t, String fqnOfInvokingClass) {
??? if(PlatformInfo.hasStackTraceElement()) {
????? StackTraceElementExtractor.extract(this, t, fqnOfInvokingClass);
??? } else {
????? LegacyExtractor.extract(this, t, fqnOfInvokingClass);?
??? }
? }

可以看到,Log4J首先判断Java平台是否支持StackTraceElement,如果是,那么用StackTraceElementExtractor,否则使用原来的LegacyExtractor。
下面来看StackTraceElementExtractor.java

/**
* A faster extractor based on StackTraceElements introduced in JDK 1.4.
*
* The present code uses reflection. Thus, it should compile on all platforms.
*
* @author Martin Schulz
* @author Ceki G&amp;lc&amp;
*
*/
public class StackTraceElementExtractor {
? protected static boolean haveStackTraceElement = false;
? private static Method getStackTrace = null;
? private static Method getClassName = null;
? private static Method getFileName = null;
? private static Method getMethodName = null;
? private static Method getLineNumber = null;
…. // 以下代码省略

可以看到,Log4J 1.3仍然兼容JDK1.3,而且为JDK1.4也做了相应的优化。
五、JDK1.5的Thread Stack Trace
JDK1.5在Thread类里面引入了getStackTrace()和getAllStackTraces()两个方法。这下子,我们不用 (new Throwable()).getStackTrace ();可以调用
Thread.getCurrentThread().getStackTrace()来获得当前线程的运行栈信息。不仅如此,只要权限允许,还可以获得其它线程的运行栈信息。

??? /**
???? * Returns an array of stack trace elements representing the stack dump
???? * of this thread.? This method will return a zero-length array if
???? * this thread has not started or has terminated.
???? * If the returned array is of non-zero length then the first element of
???? * the array represents the top of the stack, which is the most recent
???? * method invocation in the sequence.? The last element of the array
???? * represents the bottom of the stack, which is the least recent method
???? * invocation in the sequence.
???? *
???? * <p>If there is a security manager, and this thread is not
???? * the current thread, then the security manager's
???? * <tt>checkPermission</tt> method is called with a
???? * <tt>RuntimePermission("getStackTrace")</tt> permission
???? * to see if it's ok to get the stack trace.
???? *
???? * <p>Some virtual machines may, under some circumstances, omit one
???? * or more stack frames from the stack trace.? In the extreme case,
???? * a virtual machine that has no stack trace information concerning
???? * this thread is permitted to return a zero-length array from this
???? * method.?
???? *
???? * @return an array of <tt>StackTraceElement</tt>,
???? * each represents one stack frame.
???? *
???? * @since 1.5
???? */
??? public StackTraceElement[] getStackTrace() {
??????? if (this != Thread.currentThread()) {
??????????? // check for getStackTrace permission
??????????? SecurityManager security = System.getSecurityManager();
??????????? if (security != null) {
??????????????? security.checkPermission(
??????????????????? SecurityConstants.GET_STACK_TRACE_PERMISSION);
??????????? }
??????? }

??????? if (!isAlive()) {
??????????? return EMPTY_STACK_TRACE;
??????? }

??????? Thread[] threads = new Thread[1];
??????? threads[0] = this;
??????? StackTraceElement[][] result = dumpThreads(threads);
??????? return result[0];
??? }

??? /**
???? * Returns a map of stack traces for all live threads.
???? *
???? * @since 1.5
???? */
??? public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
??????? // check for getStackTrace permission
??????? // Get a snapshot of the list of all threads
??? }

六、总结
从总的发展趋势来看,JDK不仅提供越来越多、越来越强的功能,而且暴露给用户的控制方法越来越多,越来越强大。

?

package test;

public class Main {
??? public static void main(String[] args) {
??????? new Test().test();
??? }
}

package test;

import java.util.Map;
import sun.reflect.Reflection;

public class Test {
??? public void test() {
??????? System.out.println("------------Reflection.getCallerClass(i)------------");
??????? System.out.println("Reflection.getCallerClass(0)=" + Reflection.getCallerClass(0));
??????? System.out.println("Reflection.getCallerClass(1)=" + Reflection.getCallerClass(1));
??????? System.out.println("Reflection.getCallerClass(2)=" + Reflection.getCallerClass(2));
??????? System.out.println("Reflection.getCallerClass(3)=" + Reflection.getCallerClass(3));
??????? System.out.println("Reflection.getCallerClass(4)=" + Reflection.getCallerClass(4));

??????? System.out.println("\n------------new Throwable().getStackTrace()------------");
??????? StackTraceElement[] elements = new Throwable().getStackTrace();
??????? for (int i = 0; i < elements.length; i++) {
??????????? System.out.println(elements[i]);
??????? }

??????? System.out.println("\n------------Thread.currentThread().getStackTrace()------------");
??????? elements = Thread.currentThread().getStackTrace();
??????? for (int i = 0; i < elements.length; i++) {
??????????? System.out.println(elements[i]);
??????? }

??????? System.out.println("\n------------Thread.getAllStackTraces()------------");
??????? Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
??????? elements = map.get(Thread.currentThread());
??????? for (int i = 0; i < elements.length; i++) {
??????????? System.out.println(elements[i]);
??????? }
??? }

}

运行结果:

------------Reflection.getCallerClass(i)------------
Reflection.getCallerClass(0)=class sun.reflect.Reflection
Reflection.getCallerClass(1)=class test.Test
Reflection.getCallerClass(2)=class test.Main
Reflection.getCallerClass(3)=null
Reflection.getCallerClass(4)=null

------------new Throwable().getStackTrace()------------
test.Test.test(Test.java:33)
test.Main.main(Main.java:5)

------------Thread.currentThread().getStackTrace()------------
java.lang.Thread.getStackTrace(Thread.java:1426)
test.Test.test(Test.java:39)
test.Main.main(Main.java:5)

------------Thread.getAllStackTraces()------------
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1477)
test.Test.test(Test.java:45)
test.Main.main(Main.java:5)

热点排行