Using InheritableThreadLocal<T> with a carefully crafted
@Override protected T childValue(T parentValue) {
// Use Thread.currentThread() -- the parent -- to make a return value.
}
makes it possible for threads you have no control over to pass a reference to themselves to any child threads they create -- this will be the closest thing their children have to a parent.
As mentioned by Gray, keeping such references could hinder GC, so wrapping them in a WeakReference<Thread> could be necessary.
Here is an example where each thread knows its full ancestry, unless ancestors are dead and buried by 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();
});
}
}
This example results in the following output on my machine:
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.
I'm not sure how generally useful this is, but it can be used to, e.g., hierarchically display what each of a number of threads (over which you have no control) have printed to System.out or logged with java.util.Logger; this is something you would want to implement as part of a test framework with parallel test runs, for instance.