Working with Virtual Threads (Java 21+)

1. Creating Virtual Threads (Thread.startVirtualThread)

APIUse
Thread.startVirtualThread(r)Quick start
Thread.ofVirtual().start(r)Builder, named
Thread.ofVirtual().unstarted(r)Manual start

2. Using Executors.newVirtualThreadPerTaskExecutor

AspectDetail
Per taskOne virtual thread per submitted task
No poolingVirtual threads are cheap
AutoCloseableUse try-with-resources

Example: Per-task executor

try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
    List<Future<String>> results = exec.invokeAll(tasks);
}

3. Using Thread.ofVirtual Builder

Builder MethodUse
name(prefix, counter)Diagnostic naming
uncaughtExceptionHandler(h)Error handling
inheritInheritableThreadLocals(b)TL inheritance
factory()Get ThreadFactory

4. Understanding Virtual Thread Scheduling

Virtual Thread (cheap, 100Ks possible)
       ↕ mount/unmount
Carrier Thread (platform, ForkJoinPool)
       ↕
Operating System
      
AspectDetail
SchedulerDedicated ForkJoinPool
MountVT runs on a carrier
UnmountOn blocking I/O — carrier free

5. Avoiding Pinning

CauseMitigation
synchronized blockingUse ReentrantLock instead
JNI/native callUnavoidable — carrier blocked
Detect-Djdk.tracePinnedThreads=full

6. Using Scoped Values

APIUse
ScopedValue.newInstance()Define
ScopedValue.where(sv, val).run(r)Set in scope
sv.get()Read in scope
vs ThreadLocalImmutable, scope-bound, no leak

7. Understanding Performance Benefits

MetricVirtualPlatform
Memory~few KB stack~1 MB stack
CreationMicrosecondsMilliseconds
Max threadsMillionsThousands
Best forBlocking I/O concurrencyCPU-bound

8. Migrating from Platform Threads

ReplaceWith
Fixed thread poolVirtual-thread-per-task executor
synchronizedReentrantLock
ThreadLocal heavy useScopedValue
Caching expensive objectsNo need — VTs are cheap

9. Debugging Virtual Threads

ToolUse
Thread dump (jcmd)Includes VT stacks
JFR VirtualThreadStart/EndLifecycle events
JFR VirtualThreadPinnedPinning detector
IDEDebugger supports VTs

10. Understanding Carrier Threads

AspectDetail
TypePlatform thread in dedicated FJP
CountDefault = available cores
Tune-Djdk.virtualThreadScheduler.parallelism=N

11. Using Virtual Threads with Blocking I/O

OperationBehavior
Socket / file I/OUnmounts (carrier free)
Park (LockSupport)Unmounts
SleepUnmounts
synchronizedPins (until JEP fix)

12. Understanding Structured Concurrency Integration

ComboDetail
VT + StructuredTaskScopeIdiomatic concurrent code
CancellationScope shutdown propagates
Error handlingShutdownOnFailure aggregates