ArchitectureA supervised JVM-class runtime — OLTP on seven engines, OLAP on three. AI-native, MCP-native, observable as plain SQL.Read the architecture
Está viendo la edición Perú. Está viendo la edición Colombia. You're viewing the Pakistan edition. Cambiar a la edición global →Cambiar a la edición global →Switch to the global edition →

VirtualThreadExecutor — JEP 444 virtual threads become first-class platform infrastructure

A platform-wide VirtualThreadExecutor lands on the runtime, replacing every newVirtualThreadPerTaskExecutor call-site with a single supervised executor that carries graceful shutdown, structured cancellation and a uniform observability surface. JEP 444 virtual threads become first-class infrastructure on every async path.

JEP 444 finalized virtual threads as a production feature in JDK 21, and the platform has been on JDK 21+ since the March 2024 upgrade. This release closes the second-order question : how should the runtime use virtual threads systematically, rather than ad-hoc per call-site. The answer ships as VirtualThreadExecutor — a single platform-managed executor that every async path now routes through.

What's wrong with raw newVirtualThreadPerTaskExecutor()

The JDK API is correct but bare. A call site that writes Executors.newVirtualThreadPerTaskExecutor() gets a working executor and four problems for free :

  • No graceful shutdown. Closing the executor at JVM shutdown either drops in-flight work silently or hangs the shutdown waiting for tasks that nobody is tracking.
  • No cancellation propagation. A cancellation upstream does not flow into the executor's pending submissions ; orphaned tasks accumulate.
  • No observability. Each call-site executor has its own identity ; the platform's running-tasks dashboard cannot enumerate them, JFR cannot tag them, JMX cannot count them.
  • No shutdown ordering. Async work needs to drain before the database pools tear down — otherwise the last virtual threads die holding connections that have already been forcibly closed. Per-site executors have no shared ordering mechanism.

What VirtualThreadExecutor adds on top

  • One supervised executor. VirtualThreadExecutor.EXECUTOR is the singular entry point for "I need a virtual thread." Every CompletableFuture, every async dispatcher, every fire-and-forget worker submits here. Counting the platform's virtual-thread usage becomes "ask the executor."
  • Graceful shutdown built in. The executor registers itself with VMShutdownManager ; on JVM stop the manager drains it with a bounded timeout, surfaces the unfinished-task count to operators, and only then proceeds to tear down the database pools.
  • Cancellation that propagates. Submission returns a tracked handle ; cancelling the handle cancels the virtual thread cleanly, with the standard JDK InterruptedException path. No orphaned work.
  • Observability through JFR and JMX. Every submission emits a JFR event ; the running-task count is a JMX gauge ; long-running virtual threads surface in the platform's jvm.running_tasks table the same way platform threads do.

Where it lands inside the platform

The migration is single-line at each call site — newVirtualThreadPerTaskExecutor()VirtualThreadExecutor.EXECUTOR — applied across ~50 sites in this release and growing. The first beneficiaries :

  • JDBC pool tier-3 connection creation. New connections materialise on the virtual-thread executor, so pool growth does not block on platform threads. Three thousand pools growing simultaneously cost kilobytes of stack each rather than megabytes.
  • AI agent parallel tool execution. Multi-tool turns dispatch tools concurrently on virtual threads ; the executor's cancellation surface lets an agent cancel() cleanly mid-turn.
  • Microservice gRPC client calls. Each outbound call to the eighteen-service mesh is a virtual thread ; the executor handles back-pressure when downstream services slow.
  • Server-Sent Events streaming. Each active SSE connection holds a virtual thread for the duration of the stream ; thousands of concurrent SSE clients are sustainable without exhausting the OS thread budget.

Virtual threads stop being a per-call-site decision and become a platform property — opt-in is the absence of VirtualThreadExecutor, not its presence. The shutdown-ordering work that landed in the same window (Phase 3 of the shutdown-order refactor, January 2026) closes the operational loop : the executor knows when to drain, the database pools know when to wait, the JVM exits cleanly even under deep async load.

See the feature →

← All posts