I am trying to figure out how can I copy a ThreadLocal value in Java 8 parallel stream.
So if we consider this:
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadContext.set("MAIN");
System.out.printf("Main Thread: %s\n", ThreadContext.get());
IntStream.range(0,8).boxed().parallel().forEach(n -> {
System.out.printf("Parallel Consumer - %d: %s\n", n, ThreadContext.get());
});
}
private static class ThreadContext {
private static ThreadLocal<String> val = ThreadLocal.withInitial(() -> "empty");
public ThreadContext() {
}
public static String get() {
return val.get();
}
public static void set(String x) {
ThreadContext.val.set(x);
}
}
}
Which outputs
Main Thread: MAIN
Parallel Consumer - 5: MAIN
Parallel Consumer - 4: MAIN
Parallel Consumer - 7: empty
Parallel Consumer - 3: empty
Parallel Consumer - 1: empty
Parallel Consumer - 6: empty
Parallel Consumer - 2: empty
Parallel Consumer - 0: MAIN
Is there a way for me to clone the ThreadLocal from main() method into the threads which are spawned for each parallel execution ?
Such so that my result is:
Main Thread: MAIN
Parallel Consumer - 5: MAIN
Parallel Consumer - 4: MAIN
Parallel Consumer - 7: MAIN
Parallel Consumer - 3: MAIN
Parallel Consumer - 1: MAIN
Parallel Consumer - 6: MAIN
Parallel Consumer - 2: MAIN
Parallel Consumer - 0: MAIN
instead of the first one?
Java 8 introduced the Stream API that makes it easy to iterate over collections as streams of data. It's also very easy to create streams that execute in parallel and make use of multiple processor cores. We might think that it's always faster to divide the work on more cores.
1. Parallel Streams can actually slow you down. Java 8 brings the promise of parallelism as one of the most anticipated new features.
As Louis has stated in the comments, your example can very well be reduced to capturing the value of a local variable in the lambda expression
public static void main(String[] args) {
String value = "MAIN";
System.out.printf("Main Thread: %s\n", value);
IntStream.range(0,8).boxed().parallel().forEach(n -> {
System.out.printf("Parallel Consumer - %d: %s\n", n, value);
});
}
It's not obvious from your example what the full use case is.
If you know exactly which threads will be started from your main thread, you may consider using an InheritableThreadLocal
This class extends
ThreadLocal
to provide inheritance of values from parent thread to child thread: when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values.
In your case, declaring val
as an InheritableThreadLocal
, since the Thread
instances created for parallel()
within the ForkJoinPool#commonPool()
are created lazily, they would all inherit from the value set
in the main
method (and thread).
This wouldn't be the case if you somehow used the commonPool
(or whatever pool the parallel
terminal operation was called on) before you set the InhertiableThreadLocal
value in the origin thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With