1

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?
michid
  • 10,536
  • 3
  • 32
  • 59
  • 1
    This blocks in JDK14 if substitute `var thread = new Thread(task); thread.start();`- so deadlock is not to do with virtual threads. Its more likely that the lambda is converted to static method of the class, and class isn't initialised at the point you call it from it's own static code. – DuncG Mar 13 '23 at 11:02
  • There’s no behavioral difference between virtual threads and platform threads in this regard and this issue is known as long as we have lambda expressions in Java. And if you use a “plain `Runnable`” that calls back a static method in the outer class, you’ll get exactly the same behavior. – Holger Mar 17 '23 at 11:37

0 Answers0