When you're in a "tight loop" (e.g. a matrix multiplication, which is basically 3 nested loops that only load data, do math, write data), Java's virtual threads just won't yield. So if you write your app in the "wrong" way, you lose concurrency.
There's a lot of discussion about this from the Go side. The original issue was this one: runtime: tight loops should be preemptiblehttps://github.com/golang/go/issues/10958
> it's possible to write a tight loop (e.g., a numerical kernel or a spin on an atomic) with no calls or allocation that arbitrarily delays preemption. This can result in arbitrarily long pause times as the GC waits for all goroutines to stop.
> has put significant effort into prototyping cooperative preemption points in loops, which is one way to solve this problem. However, even sophisticated approaches to this led to unacceptable slow-downs in tight loops (where slow-downs are generally least acceptable).
> I propose that the Go implementation switch to non-cooperative preemption using stack and register maps at (essentially) every instruction. This would allow goroutines to be preempted without explicit
preemption checks. This approach will solve the problem of delayed preemption with zero run-time overhead and have side benefits for debugger function calls
I 100% expect Java will have to do through the same evolution. But first they'll probably try to deny reality for a few years. Funny enough, same as has happened with Go and generics.
I doubt it, since they already did that journey long ago and are now adding virtual threads next to ordinary threads that replaced the original green threads. If you put your long running work into the virtual kind of threadpool, a timing sentinel can easily warn you and after noticing that you easily use the normal threadpool instead.
VertX framework had such a sentinel, but migrating code from the async futures to a normal threadpool can be a bit tedious if your design is poor.
Basically a leaky abstraction, you have to think whether your code will block or not.
Go went through the same process, before eventually figuring out that they do need to make their lightweight threads (goroutines) preemptive.