Note: I'm well aware that creating threads in static initialisers is discouraged. Yet, exploring edge cases can provide interesting insight and uncover undesired behaviour or bugs. Hence this question.
Experimenting with virtual threads in Java 20, I discovered some inconsistent behaviour when creating a virtual thread in a static initialiser that does nothing and is joined immediately:
static {
Runnable task = () -> {};
var thread = Thread.startVirtualThread(task);
try {
thread.join();
} catch (InterruptedException e) {}
}
Here the call to join()
blocks indefinitely. This is different from using a platform thread where join()
returns immediately. The blocking behaviour is also very sensitive to the task
. There is no blocking if task
is an instance of a Runnable
instead of a lambda. Neither is there any blocking if task = SomeOtherClass.NOOP
, where SomeOtherClass.NOOP = () -> {}
.
Questions:
- While I have some broad understanding why such behaviour might happen with virtual threads vs. real threads, is such behaviour documented or at least anticipated?
- What makes using those noop lambdas inline so special such that only these cause the blocking? It looks like they lead to a static initialisation "deadlock". But why wouldn't that happen when using a plain
Runnable
instead?