Skip to content

Commit 4140195

Browse files
committed
docs: add detailed explanation of Java Virtual Threads (Project Loom)
Comprehensive overview of how Java 21+ virtual threads decouple Java threads from OS threads. Includes layered ASCII diagrams showing JVM scheduling, carrier threads, and M:N mapping. Explains how the JVM now performs user-mode scheduling, enabling millions of lightweight threads with minimal blocking and kernel interaction. Signed-off-by: https://github.com/Someshdiwan <[email protected]>
1 parent 210d97f commit 4140195

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
How Java Virtual Threads (from Project Loom, standardized in Java 21+)
2+
revolutionize the threading model by decoupling Java threads from OS threads.
3+
4+
This is one of the biggest structural evolutions since the JVM was created.
5+
6+
7+
8+
1. Quick Idea Before the Diagram
9+
10+
Traditional threads (what your C pthreads and Thread objects use) are 1:1 mapped:
11+
one Java thread → one OS thread (pthread_create())
12+
13+
Virtual threads introduce an M:N model: many Java virtual threads → multiplexed over fewer carrier (OS) threads
14+
15+
The JVM acts like a mini operating system, scheduling virtual threads in user space —
16+
no kernel involvement during blocking operations.
17+
18+
That means you can have millions of concurrent tasks without melting your system.
19+
20+
21+
22+
2. ASCII Diagram — Virtual Threads in the JVM (Project Loom)
23+
24+
┌────────────────────────────────────────────┐
25+
│ Your Java Application │
26+
│--------------------------------------------│
27+
│ Creates many Virtual Threads │
28+
│ e.g., 1 million concurrent tasks │
29+
└────────────────────────────────────────────┘
30+
31+
32+
┌───────────────────────────────────────────────────────────┐
33+
│ JVM Scheduler (User-Mode) │
34+
│-----------------------------------------------------------│
35+
│ Manages VThreads (lightweight fibers) │
36+
│ Handles blocking, resuming, parking threads internally │
37+
│ M:N scheduling — maps many virtual threads onto few OS │
38+
│ carrier threads │
39+
└──────────────┬──────────────┬──────────────┬──────────────┘
40+
│ │ │
41+
▼ ▼ ▼
42+
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
43+
│ Carrier Thread │ │ Carrier Thread │ │ Carrier Thread │
44+
│ (Real OS │ │ (pthread) │ │ (pthread) │
45+
│ Thread) │ │ │ │ │
46+
└────────────────┘ └────────────────┘ └────────────────┘
47+
│ │ │
48+
▼ ▼ ▼
49+
┌──────────────────────────────────────────────────────────┐
50+
│ Operating System (Kernel Layer) │
51+
│----------------------------------------------------------│
52+
│ CPU Scheduler, Context Switching, Syscalls, etc. │
53+
│ Only sees carrier threads, not virtual threads │
54+
└──────────────────────────────────────────────────────────┘
55+
56+
57+
58+
3. What’s Happening Here
59+
60+
Step 1: Your code creates virtual threads
61+
62+
Thread.startVirtualThread(() -> {
63+
// some blocking IO or computation
64+
});
65+
66+
Each of these is a virtual thread object managed by the JVM, not the OS.
67+
Creating one takes microseconds and bytes, compared to milliseconds and megabytes for a native thread.
68+
69+
70+
71+
Step 2: The JVM runs a small pool of “carrier” OS threads
72+
• These are true native threads (pthreads on Linux/macOS).
73+
• They act as execution engines for many virtual threads.
74+
• When one virtual thread blocks (say, waiting for IO), the JVM parks it and lets another one run — no kernel-level blocking.
75+
76+
This means the JVM is now its own cooperative scheduler.
77+
78+
79+
80+
Step 3: The OS still only sees a few threads
81+
82+
Even if your app spawns millions of virtual threads, the OS might only see 8 carrier threads, one per CPU core.
83+
The JVM efficiently multiplexes all those user-level threads across them.
84+
85+
86+
87+
4. Why This Is a Big Deal
88+
89+
┌────────────────────────────┬──────────────────────────────────────────────┬────────────────────────────────────────┐
90+
│ Feature │ Traditional Threads │ Virtual Threads │
91+
├────────────────────────────┼──────────────────────────────────────────────┼────────────────────────────────────────┤
92+
│ Mapping │ 1:1 │ M:N │
93+
│ Creation cost │ High (~1MB stack each) │ Very low (heap-allocated stack, tiny) │
94+
│ Blocking IO │ OS-level block │ JVM parks thread, frees carrier │
95+
│ Scalability │ Thousands max │ Millions feasible │
96+
│ Scheduling │ OS kernel │ JVM (user-mode) │
97+
│ Context switch │ Kernel transition │ In-memory, very fast │
98+
└────────────────────────────┴──────────────────────────────────────────────┴────────────────────────────────────────┘
99+
100+
The JVM now acts like a lightweight operating system on top of your real OS —
101+
it does what pthread does, but for Java, with much less cost.
102+
103+
104+
105+
5. Visualization of Multiplexing in Motion
106+
107+
Virtual Threads waiting to run:
108+
┌──────────────────────────────────────────────┐
109+
│ VThread#1 │ VThread#2 │ VThread#3 │ ... │
110+
└──────────────────────────────────────────────┘
111+
112+
↓ (Scheduled by JVM user-mode scheduler)
113+
114+
Carrier Threads executing (mapped to CPUs):
115+
┌──────────────┬──────────────┬──────────────┐
116+
│ Carrier#1 │ Carrier#2 │ Carrier#3 │
117+
│ (pthread) │ (pthread) │ (pthread) │
118+
└──────────────┴──────────────┴──────────────┘
119+
120+
When VThread#1 blocks (e.g., network read), it’s parked.
121+
Carrier#1 picks up VThread#42 immediately — no kernel wait.
122+
123+
This is similar to goroutines in Go or async tasks in Node, but with real Java Thread APIs, not new syntax.
124+
125+
126+
127+
6. How to Use It in Java 21+
128+
129+
Example code:
130+
131+
public class VirtualThreadDemo {
132+
public static void main(String[] args) throws InterruptedException {
133+
try (var executor = java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor()) {
134+
for (int i = 0; i < 1_000_000; i++) {
135+
executor.submit(() -> {
136+
Thread.sleep(10);
137+
return Thread.currentThread().getName();
138+
});
139+
}
140+
} // Auto-shutdown after all tasks finish
141+
}
142+
}
143+
144+
That code launches a million concurrent tasks, each as a true Java thread —
145+
but the JVM runs them using just a handful of carrier pthreads.
146+
147+
148+
149+
7. Recap Summary
150+
151+
┌────────────────────────────┬──────────────────────────────────────────────┬────────────────────────────────────────┐
152+
│ Layer │ Classic Threads │ Virtual Threads │
153+
├────────────────────────────┼──────────────────────────────────────────────┼────────────────────────────────────────┤
154+
│ Thread model │ 1 Java = 1 OS thread │ Many Java = few OS threads │
155+
│ Scheduler │ OS kernel │ JVM user-mode scheduler │
156+
│ Blocking │ Expensive │ Cheap (JVM parks) │
157+
│ Creation │ Heavy │ Lightweight │
158+
│ Best for │ CPU-bound tasks │ IO-bound, massively concurrent apps │
159+
└────────────────────────────┴──────────────────────────────────────────────┴────────────────────────────────────────┘
160+
161+
162+
163+
Virtual Threads essentially make Java’s concurrency model as scalable as Go but
164+
as simple as C’s pthreads, combining both worlds — the low-level precision of native threads and
165+
the high-level safety and efficiency of managed scheduling.

0 commit comments

Comments
 (0)