我可以获得当前线程产生的类和方法的名称吗?

Posted

技术标签:

【中文标题】我可以获得当前线程产生的类和方法的名称吗?【英文标题】:Can I get the name of the class and method within which the current thread was spawned? 【发布时间】:2011-06-15 21:50:56 【问题描述】:

这是一个不寻常的问题:是否可以获得最初产生当前正在运行的线程的类/方法?运行堆栈跟踪自然会在当前线程的调用堆栈顶部停止。

【问题讨论】:

我希望你这样做是为了一些 hacky 调试,而不是任何重要的事情。 哈哈,你猜对了。自从我开始走这条路以来,我就没有停止过鼻子:-D 相关(但不相同):Forging a stack trace in Java @Mark,它可以用于安全原因,虽然我确信它不是。 【参考方案1】:

是的,您可以: 我为您提供 2 种方法:一种标准方法和一种半破解方法。

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

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

Hacky one:在主线程中安装一个 InheritableThreadLocal(名为 main 并通过方法 main(String[] 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] - 创建堆栈:[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] - 创建堆栈:[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)]

祝你好运,黑客愉快。

【讨论】:

带有 InheritableThreadLocal 的解决方案是一个漂亮的解决方案。我想知道如果 main() 的源不可用(例如在应用程序服务器的情况下)是否可以使用它 当然,main() 是应用程序中第一个(通常是 erm 几乎)调用的方法。如果它不可用,则在初始化 threadLocal 后,所有子(和孙子++)线程都将是可跟踪的。简而言之,尽快完成初始化部分。【参考方案2】:

通常不会。

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

但通常您会改用线程池,您想知道哪个方法将任务提交给执行器的execute(),而不是哪个方法启动了线程。

【讨论】:

如果你控制了池,那就没什么好奇怪的了,你甚至可以控制线程工厂,所以它就是线程工厂。 @bestsss:是的。我的观点是,这个线程工厂方法通常不是你真正想知道的——你想知道谁(哪个方法)将工作提交给线程池(或者谁创建了可运行对象)。为此,您的线程池必须将元数据添加到所有任务中,并以某种方式提供检索它的方法。 ŭlo,你在AbstractExecutorService 中有newTaskFor(...) 来装饰任务。相反,知道第一个任务何时产生线程可能非常重要。虽然原因不明,但新创建的线程从父线程继承了相当多的内容,并且需要一个勤奋的 ThreadFactory 来防止泄漏(ClassLoader、AccessControlContext、ThreadGroup 等) newTaskFor 的好主意...但是默认线程池 (ThreadPoolExecutor) 不会在那里存储有关调用线程的任何元数据,是吗?我没有想到线程泄漏的影响,好点。【参考方案3】:

不,这是不可能的。可能实现这一点的方法是使用 AspectJ 在Thread.start() 上提供before 建议...当然,您可以继承Thread,修补JDK 代码(不同的引导CLASSPATH)等,但AspectJ 似乎是最便携和优雅的解决方案。

【讨论】:

【参考方案4】:

与生成对象的合作是可能的 - 它可以将信息写入 Thread 对象(例如,写入线程的名称,如果您想要一种 hacky 方式,或者您可以为此目的创建一个字段),您可以稍后用于调试等。但我认为没有任何“内置”方法可以获取此信息。

【讨论】:

但我认为没有任何“内置”方法可以获取此信息,正是出于这个原因创建了 SecurityManager,以跟踪对潜在安全风险的调用代码。

以上是关于我可以获得当前线程产生的类和方法的名称吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java面试08|Java重要的类和相关的方法

[转]如何写出线程安全的类和函数

java中可以根据线程名而不是id获得想要的线程吗

PHP中如何获得当前类的名称,而实例化后获取子类的名称

VC如何获得当前线程的ID

取得和设置线程的名称