【问题标题】:How to find the name of the parent thread?如何找到父线程的名称?
【发布时间】:2012-07-30 13:39:12
【问题描述】:

我知道当我们谈论流程时,我们可以有“父母”和“孩子”。但是有可能得到父母Thread 的名字吗?

我进行了研究,但我只找到了.Net的答案


编辑:我尝试设置名称:

public class Main {

    public static void main(String[] args) {
        Thread r = new ThreadA();
        r.start();
    }

}



public class ThreadA extends Thread {
    public void run() {
        Thread.currentThread().setName("Thread A");
        System.out.println("Here  " + Thread.currentThread().getName());
        Thread r = new ThreadB();
        r.setName(Thread.currentThread().getName());
        r.start();
    }
}

public class ThreadB extends Thread {
    public void run() {
        Thread.currentThread().setName("Thread B");
        System.out.println("Here " + Thread.currentThread().getName());
        Thread r = new ThreadC();
        r.setName(Thread.currentThread().getName());
        r.start();
    }
}

public class ThreadC extends Thread {
    public void run() {
        Thread.currentThread().setName("Thread C");
        System.out.println("Here " + Thread.currentThread().getName());
    }
}

【问题讨论】:

  • 你的意思是ThreadGroup的名字包含Thread吗?
  • 您可能需要解释为什么您需要此信息。您可能会发现有更好的解决方案。
  • @Gray 我需要在控制流中一起使用的线程具有相同的名称。

标签: java multithreading


【解决方案1】:

我知道当我们谈论流程时,我们可以有“父母”和“孩子”。但是是否可以获取父线程名称?

线程没有对父线程的引用,因此您无法从特定线程获取父线程的名称。在查看代码时,父线程用于获取守护进程状态、优先级和其他信息,但名称并未存储在新的Thread 对象中。

您提到您需要拥有线程的名称,以便您可以将那些“一起进入控制流”的线程分组。我会调查ThreadGroups。它们不经常使用,但在这种情况下您可能需要:

ThreadGroup threadGroup = new ThreadGroup("mythreadgroup");
Thread thread = new Thread(threadGroup, new Runnable() {...});
...
// then you can do such methods as
threadGroup.enumerate(...);

使用线程组,您可以将多个线程绑定在一起。当然,您也可以自己使用集合来执行此操作。


编辑:

您提到真正的问题是如何衡量分布式系统每个组件(在本例中为 RMI 处理程序)中的“时间花费”。

恐怕这里没有简单的答案。对于挂钟,您必须将每个 RMI 方法调用开始时的System.currentTimeMillis() 与结束时的时间进行比较。也可以使用下面的代码来测试线程使用的CPU时间。

ThreadInfo threadInfo =
    ManagementFactory.getThreadMXBean().getThreadCpuTime(thread.getId()); 

要获取“用户”时间,请使用 getThreadUserTime(...)。我不确定是否重复使用线程 ID,因此您可能需要做的就是将 RMI 调用中的所有线程 ID 记录在一个集合中,然后在监控线程中记录它们的 CPU 和用户时间。

我怀疑 RMI 线程有一个特定的名称,因此您的监控线程可以在线程列表中找到执行此操作的线程,但您无法确定哪个线程正在处理哪个 RMI 请求。

最后,要考虑的一件事是在过程中的多个点获取时间戳,并在调用之间传递这个long[]。这会增加一小部分数据开销,但您将能够很好地了解分布式系统各个不同部分的性能。

【讨论】:

  • 听起来像一个解决方案,谢谢!是否可以将ThreadGroup 用于 RMI 或不同 JVM 创建的线程?
  • 当然在不同的 JVM 上不可用。我认为您也无法控制 RMI 线程。它们由 JVM 自动处理。
  • 我仍然看不出这是必要的原因@trebuchet?听起来您正在尝试在圆孔中使用方钉。
  • 我不知道“圆孔中的方钉”(非母语人士)是什么意思,但听起来很可怕 :) 我需要从每个请求中收集线程。我的任务是测量(针对每个请求)在分布式系统中每个组件所花费的时间
  • 这就是你应该问的问题。 “我如何衡量在 RMI 调用中花费的时间”。这是正确的问题@trebuchet。我怀疑您将不得不在线程的开始和结束时记录挂钟。您也可以在开头和结尾使用ThreadInfo threadInfo = ManagementFactory.getThreadMXBean().getThreadCpuTime(thread.getId());(或getThreadUserTime)。
【解决方案2】:

否 - 在 Java 或 .NET 中都没有“父”线程的特定概念。但是,根据您引用的 .NET 答案,如果您自己创建线程,则始终可以在新线程的名称中给出一个名称,以指示“创建者”线程名称。

编辑:您的示例代码设置了名称​​before它开始...但然后覆盖它after它开始,忽略以前的名称。

我希望是这样的:

String currentName = Thread.currentThread.name();
Thread thread = new Thread(new RunnableC());
thread.setName("C (started by" + currentName + ")");
thread.start();

这将是唯一设置线程名称的地方。

请注意,这也使用了实现Runnable 而不是扩展Thread 的思想。这是另一回事,但在大多数情况下是首选方法。

【讨论】:

  • @trebuchet:不知道,恐怕。
【解决方案3】:

使用InheritableThreadLocal<T>精心制作

@Override protected T childValue(T parentValue) {
    // Use Thread.currentThread() -- the parent -- to make a return value.
}

使您无法控制的线程可以将自己的引用传递给它们创建的任何子线程——这将是它们的子线程与父线程最接近的东西。

正如 Gray 所说,保留此类引用可能会阻碍 GC,因此可能有必要将它们包装在 WeakReference<Thread> 中。

这是一个示例,其中每个线程都知道其完整祖先,除非祖先已死并被 GC 掩埋。

import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

import static java.lang.Thread.currentThread;

public class ThreadAncestry {

    /** Linked list holding the thread which created the current one, and its ancestry */
    static class Chain {

        final Chain ancestors;
        final WeakReference<Thread> parent;

        Chain(Chain ancestors, Thread parent) {
            this.ancestors = ancestors;
            this.parent = new WeakReference<>(parent);
        }

        @Override
        public String toString() {
            Thread parent = this.parent.get();
            return   (parent == null ? "[dead and buried]" : parent.getName())
                   + (ancestors == null ? "" : " -> " + ancestors);
        }

    }

    /** Prints the current thread's ancestry, then spawns a new thread which does the same. */
    static void spawnRecursively(InheritableThreadLocal<Chain> ancestors, int remainingSpawns) {
        System.out.println(  "The ancestors of " + currentThread().getName() + " are " + ancestors.get());
        if (remainingSpawns > 0)
            new Thread(() -> spawnRecursively(ancestors, remainingSpawns - 1)).start();
    }

    /** Uses an InheritableThreadLocal to record the ancestry of each thread as they are created. */
    public static void main(String[] args) {
        InheritableThreadLocal<Chain> ancestors = new InheritableThreadLocal<Chain>() {
            @Override
            protected Chain childValue(Chain parentValue) {
                return new Chain(parentValue, currentThread()); // This is called by the parent thread.
            }
        };

        spawnRecursively(ancestors, 3);

        IntStream.range(0, 6).parallel().forEach(
                i -> System.out.println(  i + " ran on " + currentThread().getName()
                                        + " with ancestors " + ancestors.get()));

        ExecutorService service = Executors.newSingleThreadExecutor();
        service.submit(() -> {
            System.out.println(  currentThread().getName() + " has ancestors "
                               + ancestors.get() + "; it will now attempt to kill these.");
            System.gc(); // May not work on all systems.
            System.out.println(  currentThread().getName() + " now has ancestors "
                               + ancestors.get() + " after attempting to force GC.");
            service.shutdown();
        });
    }

}

此示例在我的机器上产生以下输出:

The ancestors of main are null
The ancestors of Thread-0 are main
The ancestors of Thread-1 are Thread-0 -> main
The ancestors of Thread-2 are Thread-1 -> Thread-0 -> main
3 ran on main with ancestors null
4 ran on main with ancestors null
5 ran on ForkJoinPool.commonPool-worker-2 with ancestors main
0 ran on ForkJoinPool.commonPool-worker-3 with ancestors ForkJoinPool.commonPool-worker-1 -> main
1 ran on ForkJoinPool.commonPool-worker-1 with ancestors main
2 ran on ForkJoinPool.commonPool-worker-2 with ancestors main
pool-1-thread-1 has ancestors main; it will now attempt to kill these.
pool-1-thread-1 now has ancestors [dead and buried] after attempting to force GC.

我不确定这有多大用处,但它可以用于,例如,分层显示多个线程(您无法控制)中的每一个已打印到 System.out 或使用 @ 记录的内容987654327@;例如,这是您希望作为具有并行测试运行的测试框架的一部分来实现的东西。

【讨论】:

    【解决方案4】:

    在接受的答案中,Gray 提到线程本地变量可能是从启动另一个线程的线程继承的(即父级到子级;请注意,术语“父级”和“子级”在这里没有任何特殊的技术含义)。

    基于这个想法,似乎有一种方法可以使用InheritableThreadLocal 找出父线程:在父线程中设置的任何值(如name)都将自动在子线程中可用。


    此外,如果我们不控制子线程(例如,我们在线程中运行第三方组件,它会产生一些我们希望跟踪的线程),则可以使用它机制也。 Reflection can let us see other threads' thread locals.

    这可以让我们例如拍摄所有正在运行的线程的快照,并找出哪些线程是由我们的线程启动的,以及这些线程的子线程等——所有它们的后代。应该可以很好地用于监控目的。不确定它是否对其他任何东西都有好处。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-28
      • 2010-10-03
      • 2020-02-04
      • 1970-01-01
      • 2021-02-25
      • 2017-04-18
      • 2023-03-27
      相关资源
      最近更新 更多