如何找到父线程的名称?

时间:2012-07-30 13:39:13

标签: java multithreading

我知道在谈论流程时,我们可以拥有“父母”和“孩子”。但是有可能获得父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());
    }
}

4 个答案:

答案 0 :(得分:10)

  

我知道在谈论流程时,我们可以拥有“父母”和“孩子”。但是有可能获得父线程名称吗?

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

您提到您需要拥有线程的名称,以便您可以对那些“在控制流中聚集在一起”的内容进行分组。我会调查ThreadGroup s。它们不经常使用,但在这种情况下你可能想要:

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[]。这会增加一小部分数据开销,但您可以很好地了解分布式系统各个不同部分的性能。

答案 1 :(得分:9)

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

编辑:您的示例代码在开始之前设置名称​​ ...但是在启动后将其覆盖,忽略之前的名称。

我希望有类似的东西:

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

这将是唯一的位置,线程的名称将被设置。

请注意,这也使用了实施Runnable而不是扩展Thread的想法。这是一个单独的问题,但在大多数情况下是首选方法。

答案 2 :(得分:6)

使用精心设计的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或使用java.util.Logger登录;例如,这是您希望作为具有并行测试运行的测试框架的一部分实现的。

答案 3 :(得分:2)

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

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

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

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