Как найти имя родительского потока?



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



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





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



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());
}
}
440   4  

4 ответов:

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

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

При просмотре кода родитель используется для получения статуса демона, приоритета и другой информации, но не хранится в объекте 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 со временем от конца. Вы также можете использовать следующий код для проверки процессорного времени, используемого нитка.

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

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

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

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

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

EDIT: ваш пример кода задает имя Перед началом... но затем перезаписывает его после , игнорируя предыдущее имя.

Я ожидал бы чего-то вроде:

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

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

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

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

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

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


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

Comments

    Ничего не найдено.