-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVirtualThreads.java
More file actions
140 lines (128 loc) · 5.33 KB
/
Copy pathVirtualThreads.java
File metadata and controls
140 lines (128 loc) · 5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package Phase7_Concurrency.Multithreading;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Virtual Threads — Java 21 (JEP 444, finalised)
* ----------------------------------------------
* A virtual thread is a thread implemented by the JVM rather than the
* OS. It looks and behaves exactly like a normal Thread, but:
* <p>
*
* - Creating one is CHEAP (microseconds, kilobytes).
* - The JVM can multiplex MILLIONS of them on a small pool of
* CARRIER threads (platform threads, typically equal to your
* CPU count).
* - When a virtual thread blocks on an I/O call (or other JDK
* blocking primitive), the JVM UNMOUNTS it from its carrier so
* the carrier can run another virtual thread.
* <p>
*
* Net effect: you write old-fashioned synchronous, blocking code with
* Thread.sleep / readLine / Socket.read, and the JVM gives you the
* scalability of an event-loop runtime.
* <p>
*
* Creating them
* -------------
* Thread.startVirtualThread(Runnable r)
* Thread.ofVirtual().name("vt-1").start(r)
* Thread.ofVirtual().factory() - ThreadFactory
* Executors.newVirtualThreadPerTaskExecutor() - one VT per task
* <p>
*
* What they're great at
* ---------------------
* - I/O-bound fan-out: thousands of concurrent HTTP/DB calls.
* - Per-request handling without "reactive" obfuscation.
* - Replacing thread pools whose only purpose was to limit thread
* COUNT (not to limit upstream concurrency).
* <p>
*
* What they don't help with
* -------------------------
* - Pure CPU-bound work. The carrier count is your real parallelism.
* - Code that PINS the virtual thread to its carrier (see below).
* <p>
*
* Pinning
* -------
* A virtual thread is pinned to its carrier (can't be unmounted) when:
* - It is inside a `synchronized` block (Java 21).
* - It calls a native method via JNI.
* <p>
*
* For high-throughput VT code, replace synchronized critical sections
* around long-running operations with ReentrantLock. (Future Java
* releases plan to lift the synchronized-pinning restriction.)
* <p>
*
* ThreadLocal and virtual threads
* -------------------------------
* Each VT has its own ThreadLocal map. With millions of VTs, that can
* be expensive. Prefer Scoped Values (Java 21 preview) for per-call
* context.
*/
public class VirtualThreads {
public static void main(String[] args) throws Exception {
section("1) Smallest possible virtual thread");
Thread.startVirtualThread(() ->
System.out.println("hi from " + Thread.currentThread()));
Thread.sleep(50);
section("2) Builders — platform vs virtual");
Thread platform = Thread.ofPlatform().name("classic-1").start(() ->
System.out.println("platform: " + Thread.currentThread()));
Thread virtual = Thread.ofVirtual().name("v-1").start(() ->
System.out.println("virtual: " + Thread.currentThread()));
platform.join();
virtual.join();
section("3) Spawning 10,000 virtual threads — each sleeps 100ms");
long t0 = System.currentTimeMillis();
try (ExecutorService vts = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < 10_000; i++) {
futures.add(vts.submit(() -> {
try { Thread.sleep(Duration.ofMillis(100)); } catch (InterruptedException ignored) {}
return null;
}));
}
for (Future<?> f : futures) f.get();
}
System.out.println("10000 VTs sleeping 100ms each finished in "
+ (System.currentTimeMillis() - t0) + " ms (much faster than 10000 platform threads)");
section("4) Detecting whether a thread is virtual");
Thread.ofVirtual().start(() -> {
Thread me = Thread.currentThread();
System.out.println("isVirtual = " + me.isVirtual() + " on " + me);
}).join();
section("5) Pinning warning — synchronized around a slow op");
// Run with -Djdk.tracePinnedThreads=full to see traces.
Object lock = new Object();
Thread pinned = Thread.ofVirtual().start(() -> {
synchronized (lock) {
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
System.out.println("ran inside synchronized; this would PIN the carrier");
}
});
pinned.join();
section("6) Use ReentrantLock instead of synchronized in hot paths");
// No pinning — the VT can unmount while sleeping inside the lock.
java.util.concurrent.locks.ReentrantLock l = new java.util.concurrent.locks.ReentrantLock();
Thread better = Thread.ofVirtual().start(() -> {
l.lock();
try {
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
System.out.println("ran inside ReentrantLock — no pinning");
} finally { l.unlock(); }
});
better.join();
section("done");
}
private static void section(String title) {
System.out.println();
System.out.println("=== " + title + " ===");
}
}