2011-06-15 51 views
4

下面是一个不常见的问题:是否有可能获得最初产生当前正在运行的线程的类/方法?运行堆栈跟踪自然会停止在当前线程调用堆栈的顶部。我可以获取当前线程产生的类和方法的名称吗?

+1

我希望你这样做是因为一些冒险的调试,而不是什么重要的。 – 2011-06-15 21:52:45

+0

哈哈,你假设正确。自从我走上这条路后,我一直没有停下我的鼻子:-D – Wilco 2011-06-15 21:59:46

+0

相关(但不完全相同):[在Java中伪造栈跟踪](http://stackoverflow.com/questions/6270256/forging-a- stack-trace-in-java) – 2011-06-15 22:02:56

回答

7

是的,你可以: 我给你2种方法:一个标准和一个半劈。

大部分答案都是过度的,而它应该是Java中的内置函数。

安装安全管理器并覆盖SecurityManager.getThreadGroup(),您可以轻松获取堆栈跟踪,也可以通过重写其余方法来禁用其他的安全检查。

哈克之一:在主线程安装的InheritableThreadLocal(所述一个命名主要和方法主要(字串[] args)运行)。覆盖受保护的InheritableThreadLocal.childValue(T parentValue),就完成了。

注意:你所创建的线程和父线程(参考)的堆栈跟踪,但应该是足够的追查问题。

我决定写超级简单的示例来说明它是多么容易: 在这里你可以看到的结果。看看这个示例,我猜想我曾经在这个网站上发布的最优雅的解决方案,大多数是B/C,但它并不明显,但简单而智能。

package bestsss.util; 

import java.util.Arrays; 

public class StackInterceptor extends InheritableThreadLocal<StackTraceElement[]>{ 
    public static final StackInterceptor instance; 
    static{ 
     instance = new StackInterceptor(); 
     instance.set(new Throwable().getStackTrace()); 
    } 

    @Override 
    protected StackTraceElement[] childValue(StackTraceElement[] parentValue) { 
     return new Throwable().getStackTrace(); 
    } 

    //test// 
    public static void main(String[] args) { 
     Runnable r= new Runnable(){ 
      @Override 
      public void run() { 
       System.out.printf("%s - creation stack: %s%n", Thread.currentThread(), Arrays.toString(instance.get()).replace(',', '\n')); 
      }   
     }; 

     Thread t1 = new Thread(r, "t1"); 
     //spacer 
     Thread t2 = new Thread(r, "t2"); 
     t1.start(); 
     t2.start();  
    } 
} 
 
Thread[t1,5,main] - creation stack: [bestsss.util.StackInterceptor.childValue(StackInterceptor.java:13) 
bestsss.util.StackInterceptor.childValue(StackInterceptor.java:1) 
java.lang.ThreadLocal$ThreadLocalMap.(ThreadLocal.java:334) 
java.lang.ThreadLocal$ThreadLocalMap.(ThreadLocal.java:242) 
java.lang.ThreadLocal.createInheritedMap(ThreadLocal.java:217) 
java.lang.Thread.init(Thread.java:362) 
java.lang.Thread.(Thread.java:488) 
bestsss.util.StackInterceptor.main(StackInterceptor.java:25)] 
Thread[t2,5,main] - creation stack: [bestsss.util.StackInterceptor.childValue(StackInterceptor.java:13) 
bestsss.util.StackInterceptor.childValue(StackInterceptor.java:1) 
java.lang.ThreadLocal$ThreadLocalMap.(ThreadLocal.java:334) 
java.lang.ThreadLocal$ThreadLocalMap.(ThreadLocal.java:242) 
java.lang.ThreadLocal.createInheritedMap(ThreadLocal.java:217) 
java.lang.Thread.init(Thread.java:362) 
java.lang.Thread.(Thread.java:488) 
bestsss.util.StackInterceptor.main(StackInterceptor.java:27)] 

好运和快乐的黑客攻击。

+0

InheritableThreadLocal的解决方案是一个美丽的解决方案。我想知道如果main()的源不可用,是否可以使用它(例如,在应用程序服务器的情况下) – 2011-09-21 07:00:50

+0

当然,main()是第一个(通常几乎是erm)方法在应用程序中调用。如果它不可用,在初始化threadLocal后,所有的child(和grand-child ++)线程都会被追踪。总之尽快完成init部分。 – bestsss 2011-09-21 08:05:01

1

通常,没有。

如果你能控制你的线程的创建,你可以继承线程并注册在构造函数中的堆栈跟踪。当然,这是创建线程的方法,不一定是调用.start()的那个方法。因此更好的重写这个方法。

但是,您经常会使用线程池,而您想知道哪个方法将任务提交给执行程序的​​,而不是哪个方法启动线程。

+0

如果你控制池,没有什么值得怀疑的,你甚至可以控制threadFactory,所以它是线程工厂。 – bestsss 2011-06-16 06:52:47

+0

@bestsss:是的。我的观点是,这种线程工厂方法通常不会是你真正想知道的** - 你会想知道谁(哪个方法)将工作提交给了线程池(或谁创建了可运行对象)。为此,您的线程池将不得不向所有任务添加元数据,并以某种方式提供一种方法来检索此操作。 – 2011-06-16 12:18:49

+1

ŭlo,'AbstractExecutorService'中有'newTaskFor(...)'来装饰任务。相反,知道第一个任务产生线程的时间可能非常重要。虽然不是很明显,但为什么新创建的线程从父类继承了很多,并且需要一个勤奋的ThreadFactory来防止泄漏(ClassLoader,AccessControlContext,ThreadGroup等) – bestsss 2011-06-16 12:23:33

0

它可能与产卵对象的合作 - 它可以将信息写入到线程对象中(例如,如果你想要一个hacky的方式,或者你可以创建一个字段为你的线程的名称,稍后可以用于调试等。 但我不认为有任何'内置'的方式来获取此信息。

+1

*但我不认为有任何'内置'的方式来获取此信息*,SecurityManager的创建正是出于这个原因,追踪调用潜在的安全风险代码。 – bestsss 2011-06-16 06:55:24

+0

的确,谢谢。 – DNA 2011-06-16 20:33:11

0

不,这是不可能的。可能是实现这一目标的方法是在Thread.start()before建议使用AspectJ ...当然,你也可以继承Thread,补丁JDK代码(不同的启动CLASSPATH)等,但AspectJ的似乎是最便携和优雅的解决方案。

+0

doh,这么多工作:) – bestsss 2011-06-16 06:47:50

相关问题