Skip to content

Commit ddef260

Browse files
committed
docs: explain JVM thread-to-OS mapping and Project Loom virtual threads
Detailed walkthrough of how the JVM maps Java Thread objects to native OS threads (pthreads), how they communicate with the OS scheduler, and how synchronization bridges user code to kernel primitives. Includes diagrams showing the classic 1:1 model and the new M:N virtual thread model from Project Loom. Signed-off-by: https://github.com/Someshdiwan <[email protected]>
1 parent 43b743f commit ddef260

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
How the JVM maps your Thread objects to real operating system threads,
2+
how they communicate with the OS scheduler, and how synchronization works.
3+
4+
Java Threads and OS Threads (pthreads)
5+
6+
┌───────────────────────────────────────┐
7+
│ Your Java Program │
8+
│ (Running inside the JVM) │
9+
└───────────────────────────────────────┘
10+
11+
12+
┌────────────────────────────────────────┐
13+
│ JVM Runtime │
14+
│----------------------------------------│
15+
│ Thread Table / Thread Manager │
16+
│ ┌─────────────────────────────────┐ │
17+
│ │ Java Thread Objects (Heap) │ │
18+
│ │ + Thread ID │ │
19+
│ │ + Runnable target │ │
20+
│ │ + Stack reference │ │
21+
│ │ + State (RUNNABLE, WAITING...) │ │
22+
│ └─────────────────────────────────┘ │
23+
│ │ │ │
24+
└─────────────┼──────────────┼───────────┘
25+
│ │
26+
┌─────────────────────────────────┘ └──────────────────────────────────┐
27+
▼ ▼
28+
┌────────────────────┐ ┌────────────────────┐
29+
│ Native Thread (T1) │ <-- created via pthread_create() │ Native Thread (T2) │
30+
│ (C-level pthread) │ (mapped 1:1 with Java Thread) │ (C-level pthread) │
31+
│--------------------│ │--------------------│
32+
│ OS Thread Stack │ │ OS Thread Stack │
33+
│ Thread Registers │ │ Thread Registers │
34+
│ OS Scheduler Entry │ │ OS Scheduler Entry │
35+
└────────────────────┘ └────────────────────┘
36+
│ │
37+
▼ ▼
38+
┌───────────────────────────┐ ┌───────────────────────────┐
39+
│ Kernel / OS │ │ Kernel / OS │
40+
│---------------------------│ │---------------------------│
41+
│ CPU Scheduler (time-slice)│ ← Schedules native threads on cores → │ CPU Scheduler (time-slice)│
42+
│ Context Switching │ │ Context Switching │
43+
│ Synchronization Primitives│ ← mutexes, semaphores, futexes │ Synchronization Primitives│
44+
└───────────────────────────┘ └───────────────────────────┘
45+
46+
47+
🧠 Breakdown of the Layers
48+
49+
1️⃣ Java Thread Layer (User Code)
50+
When you write:
51+
52+
new Thread(runnable).start();
53+
54+
you’re creating a Java object (on the JVM heap) that holds metadata — name, priority, state — and
55+
a reference to your Runnable code.
56+
57+
58+
59+
2️⃣ JVM Thread Manager
60+
The JVM has an internal thread table to manage all live threads.
61+
62+
When you call .start(), the JVM:
63+
1. Allocates a native thread (via pthread_create() on Linux/macOS, CreateThread() on Windows).
64+
2. Attaches the Java thread object to it.
65+
3. Initializes a separate native stack for that thread.
66+
67+
That’s why modern JVMs (HotSpot, OpenJ9, GraalVM) are called 1:1 threading models — every Java thread = one native
68+
thread.
69+
70+
71+
72+
3️⃣ Native Thread (OS-level)
73+
Each thread runs your Java bytecode inside its own call stack.
74+
The OS scheduler time-slices them across available CPU cores, just like any C pthread.
75+
76+
So when two Java threads run concurrently:
77+
• The JVM doesn’t “fake” concurrency.
78+
• It truly runs two native threads in parallel (subject to CPU availability).
79+
80+
81+
82+
4️⃣ Kernel and CPU Scheduler
83+
At the bottom, everything hits the same real-world machinery:
84+
• The CPU scheduler decides which threads run.
85+
• The kernel provides synchronization primitives (mutexes, futexes, semaphores) that
86+
the JVM uses to implement synchronized, wait(), notify(), and Lock mechanisms.
87+
88+
89+
Essentially, when you write:
90+
91+
synchronized (obj) { ... }
92+
93+
you’re indirectly triggering an OS-level mutex or futex lock.
94+
95+
96+
97+
🧩 Java Virtual Threads (Project Loom) — The New Layer
98+
99+
In modern Java (19+ preview, 21+ stable), virtual threads were introduced —
100+
a revolutionary abstraction that changes the top layer of this diagram.
101+
102+
103+
Here’s a simplified update:
104+
105+
[ Java Virtual Thread ]
106+
107+
108+
[ JVM Scheduler (user-mode) ]
109+
110+
111+
[ Shared Native Thread (pthread) ]
112+
113+
114+
Instead of mapping every Java thread to a real OS thread (which is heavy),
115+
many virtual threads are multiplexed on a smaller pool of carrier OS threads.
116+
117+
118+
That means the JVM handles the scheduling itself, in user space, making millions of threads feasible.
119+
120+
So:
121+
• Traditional threads → 1:1 mapping to native pthreads.
122+
• Virtual threads → M:N mapping (many Java threads on fewer native threads).
123+
124+
125+
126+
Summary
127+
128+
| Concept | C (pthread) | Java Classic Threads | Java Virtual Threads |
129+
|-----------------|--------------------------|-----------------------------|----------------------------------------|
130+
| Thread Creation | pthread_create() | new Thread().start() | Thread.ofVirtual().start() |
131+
| OS Mapping | 1:1 | 1:1 | M:N |
132+
| Scheduler | OS kernel | OS kernel | JVM user-space |
133+
| Stack | Fixed native stack | Native stack | Lightweight heap stack |
134+
| Context Switch | Kernel mode | Kernel mode | User mode (cheap) inside JVM container |
135+
136+
137+
138+
This architectural stack is the same skeleton under every Java concurrency construct
139+
— Thread, ExecutorService, ForkJoinPool, CompletableFuture, and now Virtual Threads
140+
— all ride atop those native pthreads or their virtualized successors.

0 commit comments

Comments
 (0)