Virtual-Threads

7 posts in this section

Scoped Values (JEP 506): The Final ThreadLocal Replacement

The ThreadLocal Problem ThreadLocal has been the standard way to pass contextual data through a call chain without polluting method signatures since Java 1.2. Every Java developer has seen it used for things like: Web request context (user ID, correlation ID, tenant ID) Database transaction binding Security principal propagation Logging MDC (Mapped Diagnostic Context) Here is the classic pattern: public class RequestContext { // ThreadLocal stores one value per thread private static final ThreadLocal<String> CURRENT_USER = new ThreadLocal<>(); public static void set(String userId) { CURRENT_USER.

Continue reading »

Structured Concurrency (JEP 505): Preview 5 — What Changed?

Note: JEP 505 is a preview feature in Java 25 (5th preview). Enable with --enable-preview. The API has been stable for several rounds and is expected to finalize in Java 26. The Problem with Unstructured Concurrency Classic Java concurrency with ExecutorService is unstructured: you submit tasks, and those tasks have no formal relationship with the code that submitted them. This causes three recurring problems: Problem 1: Partial failure leaves orphaned tasks ExecutorService exec = Executors.

Continue reading »

Scoped Values (JEP 446): Thread-Safe Context Without ThreadLocal

Preview Feature — Requires --enable-preview at compile and runtime. The ThreadLocal Problem at Scale ThreadLocal has been the standard way to pass context (request ID, user session, database transaction) implicitly through a call stack without threading it through every method signature. It works well with a few hundred platform threads. With virtual threads, it breaks down. Problem 1 — Memory overhead with inheritance When you create a child thread with InheritableThreadLocal, Java copies the thread-local map from parent to child:

Continue reading »

Structured Concurrency (JEP 453): Safe, Readable Concurrent Code

Preview Feature — Requires --enable-preview at compile and runtime. The API stabilized significantly from Java 21 through 24 and will be finalized in a future release. The Unstructured Concurrency Problem When you submit tasks to an ExecutorService, the tasks are logically related but structurally unconnected — the executor doesn’t know they belong together: // Unstructured concurrency — looks simple, hides serious problems ExecutorService executor = Executors.newCachedThreadPool(); Future<Order> orderFuture = executor.

Continue reading »

Virtual Threads (JEP 444): A Million Threads Without the Pain

The Platform Thread Problem Every Java thread since Java 1.0 maps 1:1 to an OS thread. OS threads are heavy: Stack memory: 512 KB – 2 MB per thread (configurable with -Xss, default 512 KB on Linux) Context switch cost: ~1–10 μs per switch (kernel mode transition + cache invalidation) Hard limit: A 64-bit machine with 8 GB RAM can support roughly 8,000–16,000 OS threads before running out of stack memory This limit shapes how Java servers are built.

Continue reading »

Async Processing with @Async and Virtual Threads

Not every operation needs to complete before the response returns. Sending an email, generating a report, publishing an event — these can run in the background. Async processing keeps request latency low while the work continues. @Async — Fire and Forget @SpringBootApplication @EnableAsync public class OrderServiceApplication { } @Service @Slf4j public class NotificationService { @Async // runs in a separate thread public void sendOrderConfirmation(Order order) { log.info("Sending confirmation for order {}", order.

Continue reading »

Spring Boot Virtual Threads: Benchmarks, Pitfalls, and When NOT to Use Them

Virtual Threads landed in Java 21 as a stable feature, and Spring Boot 3.2 added first-class support with a single property. The promise: write simple blocking code and get WebFlux-level throughput. The reality is mostly true — with some important exceptions. This article covers what Virtual Threads actually are, how to enable them in Spring Boot, real benchmark numbers, the three pitfalls that will silently destroy your performance, and a decision framework for when to use them (and when not to).

Continue reading »