For decades, a Java thread mapped one-to-one to a costly operating-system thread, so you could run only a few thousand at once, and any thread that blocked on I/O, waiting for a network response or a database query, sat idle holding an expensive resource. This forced the complex non-blocking and asynchronous styles of the previous lesson just to scale. Java 21 introduced virtual threads (from Project Loom) to change this fundamentally: lightweight threads managed by the JVM, not the OS, so cheap that you can have millions.
A virtual thread runs on top of a small pool of OS threads (carriers); when it blocks, the JVM unmounts it and frees the carrier to run another virtual thread, so blocking becomes cheap. This means you can write simple, straightforward blocking code, a thread per task, that nonetheless scales to enormous concurrency. Alongside this, structured concurrency provides a way to treat a group of related concurrent tasks as a single unit with clear lifetimes and error handling.
Understanding virtual threads matters because they represent a major shift in how scalable Java concurrency is written: much of the complexity of async programming exists to avoid blocking scarce OS threads, and virtual threads remove that constraint, letting simple blocking code achieve what previously required intricate non-blocking pipelines. Grasping how they work, and when they help, is essential to writing modern, scalable Java.