I would put it more charitably as "Java Virtual Threads are new and have not seen massive use and optimization yet".
This is crucial, because Java wouldn't necessarily require the same optimizations Go needed.
Making Virtual Threads fully preemptive could be useful, but it's probably not as crucial as it was for Go.
Go does not have a native mechanism to spawn OS threads that are separate from the scheduler pool, so if you want to run a long CPU-heavy task, you can only run it on the same pool as you run your I/O-bound Goroutines. This could lead to starvation, and adding partial preemption and later full preemption was a neat way to solve that issue.
On the other hand, Java still has OS threads, so you can put those long-running CPU-bound tasks on a separate thread-pool. Yes, it means programmers need to be extra careful with the type of code they run on Virtual Threads, but it's not the same situation as Go faced: in Java they DO have a native escape hatch.
I'm not saying a preemptive scheduler won't be helpful at Java, but it just isn't as direly needed as it was with Go. One of the most painful issues with Java Virtual Threads right now is thread pinning when a synchronized method call is executed. Unfortunately, a lot of existing Java code is heavily using synchronized methods[1], so it's very easy to unknowingly introduce a method call that pins an OS thread. Preemeptive could solve this issue, but it's not the only way to solve it.
---
[1] One of my pet peeves with the Java standard library is that almost any class or method that was added before Java 5 is using synchronized methods excessively. One of the best examples is StringBuffer, the precursor of StringBuilder, where all mutating methods are synchronized, as if it was a common use case to build a string across multiple threads. I'm still running into StringBuffers today in legacy codebases, but even newer codebases tend to use synchronized methods over ReentrantLocks or atomic operations, since they're just so easy to use.
Virtual threads could be scheduled pre-emptively but currently the scheduler will wait for some kind of thread sleep to schedule another virtual thread. That's just a scheduler implementation detail and the spec is such that a time slice scheduler could be implemented.
> That means that Java programmers have to be very careful when writing code
From JEP 444:
The scheduler does not currently implement time sharing for virtual threads. Time sharing is the forceful preemption of a thread that has consumed an allotted quantity of CPU time. While time sharing can be effective at reducing the latency of some tasks when there are a relatively small number of platform threads and CPU utilization is at 100%, it is not clear that time sharing would be as effective with a million virtual threads.
Also, in this scenario, i think the current scheduler (ForkJoin Pool) will use managed blocker to compensate those pinned carrier threads.
I don't know. The language already has Thread.Yield. If your use case is such that you have starvation and care about it, it seems trivial to work around.
Still, an annoying gotcha if it hits you unexpectedly.
> Like a platform thread, a virtual thread is also an instance of java.lang.Thread. However, a virtual thread isn't tied to a specific OS thread. A virtual thread still runs code on an OS thread. However, when code running in a virtual thread calls a blocking I/O operation, the Java runtime suspends the virtual thread until it can be resumed. The OS thread associated with the suspended virtual thread is now free to perform operations for other virtual threads.
It's not been hidden at all in their presentation on virtual threads.
The OS thread that the virtual thread is mounted to can still be preempted, but that won't free up the OS thread for another virtual thread. However, if you use them for what they're intended for this shouldn't be a problem. In practice, it will be because no one can be bothered to RTFM.
All that says is the Java runtime will suspend on blocking IO, not that it _only_ suspends on blocking IO.
> what they're intended for
Java prides itself on careful and deliberate changes to eliminate foot guns, but this seems like a pretty major restriction. Usually these kinds of cooperative threads are called fibers or something else to distinguish them from truly preempt-able threads.
Expecting developers to read the minutiae of documentation (there's another restriction around synchronized blocks) is a fool's errand TBH. Principle of least surprise, etc.
As a sibling comment points out, there's nothing in what you quoted that logically implies that blocking I/O is the only reason for a virtual thread to be suspended.
"Virtual threads, however, are handled differently than platform threads. None of the existing schedulers for virtual threads uses time slices to preempt virtual threads."
The next handful of paragraphs are also interesting.
Go's goroutines are preemptive (and Go's development team went through a lot of pain to make them such).
Java's lightweight threads aren't.
Java's repeating the same mistakes that Go made (and learned from) 10 years ago.