Concurrency & Green Threads in Java
What Exactly Is a Thread?
Concurrency & Green Threads in Java

What Exactly Is a Thread?
A thread is the smallest unit of execution within a process. Each thread has its own program counter, stack, and local variables but shares heap memory with other threads in the same process. This shared memory model is what makes threads powerful for communication, and simultaneously treacherous without careful synchronization.
In a concurrent program, multiple threads progress simultaneously (or appear to, via time-slicing on a single core). True parallelism requires multiple CPU cores executing different threads at the exact same instant.
OS Threads & the JVM Threading Model
Modern Java maps each thread directly to a kernel-managed OS thread. This is the 1:1 model. i.e. one Java thread equals one native thread in the OS.
This is simple and efficient for CPU-bound work. But for I/O-bound workloads (web servers, database calls, file access), threads spend most of their time blocked. They are either waiting, doing nothing, yet consuming memory and OS scheduling overhead.
Traditional servlet containers like Apache Tomcat follow a thread-per-request model. Each HTTP request gets its own thread. If a request waits on a DB query for 200ms, that thread is blocked the entire time. Scale to 10,000 simultaneous requests, and you need 10,000 threads — each consuming ~1MB of stack memory. That’s 10GB just for stacks.
// Classic thread-per-request — one OS thread blocks per connection
public class TraditionalServer {
private static final ExecutorService pool =
Executors.newFixedThreadPool(200); // hard ceiling!
public void handleRequest(Socket socket) {
pool.submit(() -> {
try {
String data = readFromSocket(socket); // BLOCKS OS thread
String result = db.query(data); // BLOCKS OS thread
writeResponse(socket, result);
} catch (IOException e) {
e.printStackTrace();
}
});
}
// 201st concurrent request? Queued and waiting.
}
Green Threads: User-Space Scheduling
Green threads are threads scheduled entirely in user space by a runtime or virtual machine. The OS sees only a handful of native threads. The runtime multiplexes hundreds or thousands of green threads on top of them. This is the M:N threading model.
Why Green Threads Win for I/O Workloads
When a green thread performs a blocking I/O operation, the runtime intercepts the call. Instead of blocking the OS thread, the runtime suspends the green thread, saves its continuation (stack state), and schedules another green thread on the same carrier thread. When the I/O completes, the original green thread is resumed.
Key Insight: With green threads, you write simple, sequential, blocking code but at runtime, the scheduler makes it non-blocking automatically. You get the readability of synchronous code with the efficiency of async I/O.
Virtual Threads: Green Threads, Grown Up
Virtual threads are Java’s implementation of green threads in the modern JDK. They are instances of java.lang.Thread, fully compatible with existing Java APIs, but managed by the JVM rather than the OS.
The JVM uses a small pool of OS “carrier threads” (typically equal to the number of CPU cores) to run virtual threads. When a virtual thread blocks on I/O, the JVM unmounts it from the carrier thread, parking its stack on the heap and runs another virtual thread in its place.
Creating Virtual Threads
Java 21
// Method 1: Thread.ofVirtual()
Thread vt = Thread.ofVirtual()
.name("my-virtual-thread")
.start(() -> System.out.println("Hello from virtual thread!"));
// Method 2: Executor — recommended for servers
try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 100_000; i++) {
int taskId = i;
exec.submit(() -> processRequest(taskId)); // 100K threads, no problem
}
} // auto-shutdown via AutoCloseable
// Method 3: fire-and-forget
Thread.startVirtualThread(() -> {
String result = httpClient.get("https://api.example.com/data");
process(result); // blocking is fine here
});
Structured Concurrency
Alongside virtual threads, Project Loom introduces structured concurrency ,a model where the lifetime of concurrent tasks is scoped to the block that creates them. This mirrors how structured programming (if/for/while) tamed goto statements.
With structured concurrency, if a parent task is cancelled, all its child subtasks are automatically cancelled too. Errors in subtasks propagate cleanly to the parent. There are no dangling threads, no orphaned tasks, no leaked resources.
This dramatically simplifies error handling and cancellation — two of the hardest problems in concurrent programming. Code that once required careful CompletableFuture chaining becomes straightforward sequential logic.
When to Use What
Use Virtual Threads When:
Your workload is I/O-bound — web servers, microservices, database calls, REST clients, file processing. Any scenario where threads spend time waiting, not computing. Virtual threads shine when you want to write simple sequential code but need massive concurrent throughput. Java 21+ with Spring Boot 3.2+ makes this a one-liner to enable.
Stick with Platform Threads When:
Work is CPU-bound — image processing, cryptography, data crunching, machine learning inference. Here, ForkJoinPool with platform threads (or ParallelStream) remains the right tool. Creating more virtual threads than CPU cores doesn't help if all they do is compute.
Avoid These Anti-Patterns:
Don’t pool virtual threads — they are cheap to create, pooling is counterproductive. Don’t use synchronized around I/O operations in virtual-thread-heavy code (pinning risk). Don't store large objects in ThreadLocal on virtual threads — with millions of threads, that memory adds up fast.
메타데이터
- post_id
- f7ecc432a2b9
- slug
- concurrency-green-threads-in-java-f7ecc432a2b9
- url
- https://medium.com/@rashichandra2/concurrency-green-threads-in-java-f7ecc432a2b9
- canonical_url
- https://medium.com/@rashichandra2/concurrency-green-threads-in-java-f7ecc432a2b9
- author_url
- https://medium.com/@rashichandra2
- status
- ok
- fetched_at
- 2026-08-24 04:50:48