| Yes. 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 preemptible https://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. The proposed (and ultimately accepted, AFAIK) solution is described here: https://github.com/golang/go/issues/24543 > 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. |
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.