Secure & Private (Zero Data Retention)
Free Access • No Sign-Up
Multithreading & Virtual Threads (Java 21)
Java 21 revolutionized backend concurrency with Virtual Threads (Project Loom), allowing you to spawn millions of threads simultaneously.
1. Platform Threads vs. Virtual Threads
Old Way (Platform Threads): 1 Java thread = 1 Operating System thread. Heavy (1MB memory per thread). Crashing at ~5,000 threads.
Java 21 Way (Virtual Threads): Managed by the JVM. Featherweight (a few bytes each). You can run 1,000,000 virtual threads without slowing down your computer!
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// Modern Virtual Threads (Project Loom, Java 21+)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1000; i++) {
final int taskId = i;
executor.submit(() -> {
Thread.sleep(50);
return "Task " + taskId + " completed";
});
}
} // Automatically awaits completion of all virtual threads
// Thread-Safe Atomic Counter (Zero Locks)
AtomicInteger counter = new AtomicInteger(0);
int nextVal = counter.incrementAndGet();
⚠️ 5 Fatal Traps & Engineering Pitfalls
Trap #1: The Non-Atomic Increment Race Condition (count++)
count++ is not a single atomic CPU operation; it consists of three discrete steps: read, increment, write. When multiple threads execute count++ concurrently, interleaving causes lost updates. Use AtomicInteger or locks.
Trap #2: Pinning Virtual Threads with Synchronized Blocks
In Java 21 Virtual Threads (Project Loom), executing a synchronized block or native method while blocking pins the virtual thread to its underlying OS carrier thread, defeating high-concurrency scalability. Replace synchronized with ReentrantLock.
Trap #3: Deadlocks Caused by Inconsistent Lock Ordering
If Thread A acquires Lock 1 then Lock 2, while Thread B acquires Lock 2 then Lock 1, both threads will block indefinitely waiting for the other. Always enforce a strict, consistent global acquisition ordering across all locks in your architecture.
Trap #4: Unbounded Queue Thread Pool Memory Exhaustion
Using Executors.newFixedThreadPool(10) pairs the worker threads with an unbounded LinkedBlockingQueue. Under heavy incoming request loads, millions of unhandled tasks queue up in heap memory, triggering an OutOfMemoryError.
Trap #5: Confusing volatile with Atomic Operations
The volatile keyword guarantees immediate visibility of writes across CPU core caches, but provides ZERO mutual exclusion or atomicity guarantees for compound check-then-act logic.
💬 Frequently Asked Questions
What are Java 21 Virtual Threads and how do they differ from OS platform threads?
OS platform threads are heavy (~1MB stack, managed by the kernel), limiting a server to a few thousand threads. Virtual threads are lightweight user-mode threads (~few hundred bytes, managed by the JVM) allowing millions of concurrent tasks.
Why does the synchronized keyword cause virtual thread pinning in Project Loom?
In current HotSpot JVM implementations, synchronized blocks bind the virtual thread to the native OS carrier thread. When blocking inside synchronized, the carrier thread cannot be unmounted to serve other virtual threads.
How does AtomicInteger achieve thread safety without locking?
AtomicInteger uses hardware-level Compare-And-Swap (CAS) CPU assembly instructions (e.g. LOCK CMPXCHG on x86), updating values in a non-blocking loop without thread context-switching overhead.
What is the difference between volatile and synchronized in Java?
volatile guarantees memory visibility across CPU caches for read/write of a single variable without locking. synchronized provides both memory visibility and exclusive mutual exclusion across code blocks.
How can you detect and diagnose Java thread deadlocks in production?
Use JDK command-line diagnostic tools such as jcmd <pid> Thread.dump_to_file or jstack <pid> to generate thread dumps, which automatically detect cyclic locking dependencies and report the offending line numbers.