A virtual thread lets imperative Java code handle many concurrent, I/O-heavy tasks without dedicating an expensive platform thread to each task. It offers scalability benefits similar to reactive programming while preserving a familiar programming style. section 8.5
The JDK manages virtual threads and runs them on platform threads called carrier threads. When a virtual thread reaches a blocking operation, the JDK can save its state and free its carrier for another virtual thread. Once the operation completes, the original virtual thread can resume on an available carrier. Virtual threads are cheap enough to create for individual tasks rather than reuse from a pool. section 8.5 section 8.5.1
In Quarkus, annotate a method with `@RunOnVirtualThread` to run it on a virtual thread; placing the annotation on a class applies it to its methods. This is one option alongside blocking and reactive execution, which can coexist in an application. section 8.5.1 section 1.3.1
@GET
@Path("/virtualThread")
@RunOnVirtualThread
public void virtualThread() {
Log.info("Running on " + Thread.currentThread().getName());
}
A REST method logs its current thread after being marked to run on a virtual thread. section 8.5.1
Virtual threads still have performance limits:
- A virtual thread can pin its carrier during blocking work in a synchronized block or with a Java Native Interface call on its stack. The carrier then cannot run another virtual thread. section 8.5.2
- Long CPU-bound work without I/O can monopolize a carrier thread. Virtual threads are best suited to workloads with substantial I/O, not CPU-bound computation. section 8.5.2

