Вопрос по java, multithreading – Как найти имя родительского потока?

7

Я знаю, что у нас могут быть «родители» и «дети»; когда мы говорим о процессах. Но возможно ли получить родителяThread название?

Я сделал свое исследование, но я нашел ответ только для.Сеть

Изменить: я пытался установить имена:

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? Eng.Fouad
@ Серый Мне нужно одно и то же имя для потоков, которые идут вместе в потоке управления. alicjasalamon
Вы можете объяснитьwhy Вы хотите эту информацию. Вы можете найти лучшие решения. Gray

Ваш Ответ

4   ответа
9

I know we can have 'parents' and 'children' when we are talking about processes. But is it possible to get parent Thread name?

Как упоминал Джон, поток не может узнать свой родительский поток. Это важно, потому что если бы у каждого дочернего элемента была ссылка на поток, который их разветвлял, это означало бы, что в памяти будет храниться много ненужных структур потоков. Структура родительского потока не может быть восстановлена GC или использована повторно, если дочерний элемент имеет ссылку на нее.

При просмотре кода родительский объект используется для получения статуса демона, приоритета и другой информации, но не сохраняется вThread объект.

Вы упомянули, что вам нужно иметь имя потоков, чтобы вы могли сгруппировать те, которые "объединяются в потоке управления". Я бы посмотрел вThreadGroups. Они не используются слишком часто, но вы можете захотеть в этом случае:

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

С группами потоков вы можете связать несколько потоков вместе. Конечно, вы можете сделать это и с коллекцией самостоятельно.


Edit:

Вы упомянули, чтоreal Вопрос в том, как вы можете измерить «затраченное время»? в каждом компоненте распределенной системы - в этом случае обработчики RMI.

Боюсь, здесь нет простого ответа. Что касается настенных часов, вам придется сравнитьSystem.currentTimeMillis() в начале каждого вызова метода RMI со временем от конца. Вы также можете использовать следующий код для проверки процессорного времени, используемого потоком.

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

Чтобы получить «пользователя» время вы используетеgetThreadUserTime(...), Я не уверен, что идентификаторы потоков используются повторно, поэтому, возможно, все, что вам нужно сделать, это записать все идентификаторы потоков в ваших вызовах RMI в коллекции, а затем записать их ЦП и время пользователя в потоке мониторинга.

Я подозреваю, что потоки RMI имеют конкретное имя, чтобы ваш поток мониторинга мог найти потоки в списке потоков, чтобы сделать это, но вы не сможете определить, какой поток обрабатывает какой запрос RMI.

Наконец, одна вещь, которую следует рассмотреть, это взять отметки времени в нескольких точках процесса и передать этоlong[] вокруг между вызовами. Это добавит небольшой процент накладных расходов на данные, но тогда вы сможете получить представление о производительности различных частей вашей распределенной системы.

Конечно, не доступно на другой JVM. Я не думаю, что вы также можете контролировать потоки RMI. Они обрабатываются JVM автоматически.
Звучит как решение, спасибо! Можно ли использоватьThreadGroup для потоков, созданных RMI или на других виртуальных машинах? alicjasalamon
Я до сих пор не вижу причины, почему это необходимо @trebuchet? Похоже, вы пытаетесь квадратный колышек в круглое отверстие.
Любой совет, как сгруппировать такие темы? alicjasalamon
Я понятия не имею, что значит «квадратный колышек в круглой дыре» (не носитель языка), но звучит ужасно :) Мне нужно собрать нити из каждого запроса. Моя задача - измерить (для каждого запроса) время, проведенное в каждом компоненте в распределенной системе alicjasalamon
8

Нет - не существует особой концепции "родителя" поток в Java или .NET. Однако, согласно ответу .NET, на который вы ссылались, если вы сами создаете тему, вы всегда можете дать имя, которое указывает на «создателя». имя потока в имени нового потока.

РЕДАКТИРОВАТЬ: ваш пример кода устанавливает имяbefore начинается ... но потом перезаписываетafter начинается, игнорируя предыдущее имя.

Я ожидаю что-то вроде:

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

Это было быonly место имя потока будет установлен.

Обратите внимание, что это также использует идею реализацииRunnable вместо расширенияThread, Это отдельный вопрос, но в большинстве случаев это предпочтительный подход.

@trebuchet: Понятия не имею, я боюсь.
Любые идеи, как установить имя для потока, созданного RMI? alicjasalamon
2

В принятом ответе Грей упоминает, что локальные элементы потока, возможно, унаследованы от потока, который запускает другой поток (т. Е. От родительского до дочернего; обратите внимание, что термины «родительский» и «дочерний» не имеют здесь более особого технического значения) ,

Исходя из этой идеи, кажется, что есть способ выяснить родительский поток, используяInheritableThreadLocal: любое значение будет установлено в родительском (например,name) будет доступен у ребенка автоматически.


Более того, если мы не контролируем дочерние потоки (например, запускаем сторонний компонент в нашем потоке, и он порождает несколько потоков, которые мы хотим отслеживать), возможно, будет возможно использовать и этот механизм.Отражение может позволить нам увидеть другие темы & apos; нить местных жителей.

Это может позволить нам, например, сделайте снимок всех запущенных потоков и выясните, какие из них были запущены нашим потоком, а также дочерние элементы этих потоков и т. д. & # x2014; все их потомки. Должно хорошо работать для целей мониторинга. Не уверен, что это будет хорошо для чего-то еще.

5

С помощьюInheritableThreadLocal<T> с тщательно продуманным

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

позволяет потокам, которыми вы не управляете, передавать ссылку на себя любым дочерним потокам, которые они создают - это будет самое близкое, что их дочерние элементы имеют к родительскому.

Как упомянул Грей, хранение таких ссылок может помешать 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; это то, что вы хотели бы реализовать, например, как часть тестовой среды с параллельными запусками.

Похожие вопросы