What virtual threads changed about Java's concurrency model
Before virtual threads, a Java application handling many concurrent blocking operations (waiting on a database, an HTTP call) either accepted the overhead of one operating system thread per concurrent request, which does not scale past a few thousand, or adopted a reactive programming model to multiplex many logical operations onto a small thread pool. Reactive code is more efficient but noticeably harder to write, debug and reason about than straightforward synchronous code.
Virtual threads, stable since Java 21, let the JVM run a very large number of lightweight threads that block cheaply, so a team can write ordinary blocking code and still handle high concurrency without the operational cost of one OS thread per request. We now default new high-concurrency Java services to virtual threads over reactive frameworks unless there is a specific reason (existing reactive codebase, a need for fine-grained backpressure control) to do otherwise, because it keeps the code considerably easier to read and maintain.










