While playing with Java parallel streams, I experienced deadlocks when some parallel operations are done within a static initializer block.
When using a sequential Stream, everything works fine:
import java.util.Arrays;
public class Example1 {
static {
// displays the numbers from 1 to 10 ordered => no thread issue
Arrays.asList(1,2,3,4,5,6,7,8,9,10)
.forEach(s->System.out.println(s));
}
public static final void main(String[] args) {}
}
When processing the stream in parallel, everyting work (the numbers are displayed without order):
import java.util.Arrays;
public class Example2 {
static {
// displays the numbers from 1 to 10 unordered => no thread issue
Arrays.asList(1,2,3,4,5,6,7,8,9,10).parallelStream()
.forEach(s->System.out.println(s));
}
public static final void main(String[] args) {}
}
However, when processing the Stream with forEachOrdered()
, a deadlock occurs (I suppose this is related to the interaction between the main thread and the ForkJoinPool management):
import java.util.Arrays;
public class Example3 {
static {
// hangs forever (deadlock between the main thread which loads the class and the underlying ForkJoinPool which join several tasks)
Arrays.asList(1,2,3,4,5,6,7,8,9,10).parallelStream()
.forEachOrdered(s->System.out.println(s));
}
public static final void main(String[] args) {}
}
But when spawning the Stream processing in a separate Thread, everything goes well:
import java.util.Arrays;
public class Example4 {
static {
// displays the numbers from 1 to 10 ordered => no thread issue
new Thread(()->
Arrays.asList(1,2,3,4,5,6,7,8,9,10).parallelStream()
.forEachOrdered(s->System.out.println(s))
).start();
}
public static final void main(String[] args) {}
}
From what I've seen from the Thread Dump, the main Thread is waiting on the ForkJoinPool used in the .forEachOrdered()
to finish his work, but the first worker Thread in the pool is blocked waiting for something (most probably blocked by the main
thread).
I would really appreciate to understand why the deadlock occurs in some cases and not in other cases. This is obviously not due only to the usage of static initializer block, parallel stream and lambda because Example2
, Example3
and Example4
use these three concepts, but only Example3
causes a deadlock.
While this question may look like a duplicate of Why does parallel stream with lambda in static initializer cause a deadlock?, it is not. My question goes beyond the linked one as it provide Example2
for which we have static initializer block, parallel stream and lambda, but no deadlock. This is why the question title contains "may lead to deadlock but not necessarily".
Example2
for which we have static initializer block, parallel stream and lambda. Technically true, but the parallel stream doesn't run inside the initializer, so why is it relevant? – shmoselExample2
, the stream elements are processed by the main thread and by other worker threads from theForkJoinPool
, so the main Thread process the static initializer and the parallel stream). – Julien KroneggExample4
is easy to explain as its initializer doesn’t wait for the completion of the stream operation. The other scenarios are subject to stream implementation details and a JVM bug discussed in Why using parallel streams in static initializer leads to not stable deadlock. – Holger