10

我知道当我们谈论流程时,我们可以有“父母”和“孩子”。但是有可能得到父母的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

4 回答 4

13

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

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

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

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

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


编辑:

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

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

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

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

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

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

于 2012-07-30T13:48:28.323 回答
11

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

编辑:您的示例代码在开始之前设置名称......但在开始后覆盖它忽略以前的名称。

我希望是这样的:

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

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

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

于 2012-07-30T13:41:55.397 回答
7

使用InheritableThreadLocal<T>精心制作的

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

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

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

这是一个每个线程都知道其完整祖先的示例,除非祖先已死并被 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;例如,这是您希望作为具有并行测试运行的测试框架的一部分来实现的东西。

于 2018-05-05T20:23:03.947 回答
2

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

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


此外,如果我们不控制子线程(例如,我们在线程中运行第三方组件,并且它产生了一些我们希望跟踪的线程),那么也可以使用这种机制。反射可以让我们看到其他线程的线程局部变量

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

于 2016-03-09T02:58:12.087 回答