Phase 10
Java 21 LTS • Virtual Threads (Project Loom), Sequenced Collections & Record Patterns
📂 Modern Core Java \ Java 21 LTS Evolution
10.1 High-Throughput Virtual Threads (Project Loom): Carrier vs Virtual Threads
📖 Cay S. Horstmann • Core Java Volume I — Chapter 12, Section 12.1 ("Virtual Threads")
“For decades, a Java thread was directly mapped 1-to-1 to an operating system thread. Because OS threads are heavyweight—requiring roughly 1MB of memory for their call stack and expensive kernel context switches—servers could only handle a few thousand concurrent connections. Virtual threads, introduced in Java 21, are lightweight threads managed entirely by the JVM. A virtual thread has a call stack of mere hundreds of bytes that grows dynamically on the heap. You can create millions of virtual threads without exhausting system memory.”
- Carrier Mounting Mechanics Unmount on I/O: When a virtual thread executes a blocking operation (network I/O, database query), the JVM unmounts its stack to the Heap and frees the underlying OS Carrier Thread to run other tasks immediately!
-
Thread-Per-Request Revival
No Thread Pools Needed: Never pool virtual threads! They are cheap to create ($O(1)$) and discard. Use
Executors.newVirtualThreadPerTaskExecutor().
💡 Real-World Analogy: Commercial Jumbo Jets vs Passenger Taxis
• Platform OS Thread (The Jumbo Jet): Requires enormous fuel, maintenance crews, and a 2-mile runway (1MB stack). An airport can only manage 100 flights at a time.
• Virtual Thread (The Individual Passenger): Weighs almost nothing. Instead of assigning an entire jumbo jet to fly 1 person to lunch, thousands of passengers walk through a shared terminal, and jump on an available jet (Carrier Thread) only when actively flying! While waiting at the gate (blocking I/O), they step aside onto the terminal floor (Heap memory).
• Virtual Thread (The Individual Passenger): Weighs almost nothing. Instead of assigning an entire jumbo jet to fly 1 person to lunch, thousands of passengers walk through a shared terminal, and jump on an available jet (Carrier Thread) only when actively flying! While waiting at the gate (blocking I/O), they step aside onto the terminal floor (Heap memory).
⚡ Pro-Tip / Interview Memory Hook: The synchronized Pinning Trap
Pinning Warning: In Java 21, if a virtual thread enters a classic
Production Fix: Replace
synchronized (lock) block and performs blocking I/O inside, it gets pinned to its OS Carrier Thread, preventing other virtual threads from using that carrier!
Production Fix: Replace
synchronized with modern java.util.concurrent.locks.ReentrantLock in high-concurrency enterprise microservices.
📂 VirtualThreadConcurrencyDemo.java — High-Throughput Loom Architecture
import java.util.concurrent.Executors;
import java.time.Duration;
public class VirtualThreadConcurrencyDemo {
public static void main(String[] args) {
// Launching 100,000 Concurrent Virtual Threads without crashing memory!
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 1; i <= 100_000; i++) {
final int taskId = i;
executor.submit(() -> {
Thread.sleep(Duration.ofMillis(100)); // Simulated DB latency
if (taskId % 25_000 == 0) {
System.out.println("Completed task #" + taskId +
" on " + Thread.currentThread());
}
return taskId;
});
}
} // auto-waits for all tasks to complete!
System.out.println("Successfully handled 100,000 tasks concurrently!");
}
}
👁 Execution & Memory Breakdown • Project Loom Virtual Thread Mounting
Step 1 (Mounting on Carrier): The JVM schedules virtual threads onto a small pool of ForkJoinPool carrier OS threads.
Step 2 (Unmounting on Blocking I/O): When
Thread.sleep() or network socket read blocks, the continuation yields. The virtual thread stack frame unmounts to the Java Heap, allowing the carrier thread to immediately execute another virtual task!
10.2 Sequenced Collections Hierarchy & Record Patterns in Switch
📖 Cay S. Horstmann • Core Java Volume I — Chapter 9 ("Sequenced Collections in Java 21")
“For 25 years, the Collections Framework lacked a unified interface for collections with a defined encounter order. Getting the last element differed wildly: list.get(list.size() - 1), deque.getLast(), or sortedSet.last(). Java 21 fixes this architectural flaw with SequencedCollection, SequencedSet, and SequencedMap, providing uniform getFirst(), getLast(), addFirst(), addLast(), and reversed() methods across all ordered collections.”
📂 Java21ModernFeaturesDemo.java — Sequenced Collections & Record Patterns
import java.util.LinkedHashSet;
import java.util.SequencedSet;
public class Java21ModernFeaturesDemo {
record Point(int x, int y) {}
record Circle(Point center, int radius) {}
public static void main(String[] args) {
// 1. Uniform Sequenced Collections (getFirst, getLast, reversed)
SequencedSet<String> orderedNodes = new LinkedHashSet<>();
orderedNodes.add("Alpha");
orderedNodes.add("Beta");
orderedNodes.addLast("Omega");
System.out.println("First node: " + orderedNodes.getFirst());
System.out.println("Last node: " + orderedNodes.getLast());
System.out.println("Reversed view: " + orderedNodes.reversed());
// 2. Record Patterns: Deconstructing objects inside switch expressions!
Object shape = new Circle(new Point(10, 20), 15);
String description = switch (shape) {
case Circle(Point(var x, var y), var r) ->
"Circle centered at (" + x + "," + y + ") with radius " + r;
case Point(var x, var y) ->
"Single point at (" + x + "," + y + ")";
default -> "Unknown geometric shape";
};
System.out.println(description);
}
}