Lab 03: Project Loom — Virtual Threads

Time: 60 minutes | Level: Architect | Docker: docker run -it --rm zchencow/innozverse-java:latest bash


Overview

Project Loom brings virtual threads to Java 21 — lightweight threads scheduled by the JVM instead of the OS. This enables millions of concurrent threads without thread pool tuning, transforming blocking I/O into a scalable model without reactive frameworks.


Step 1: Virtual Thread Basics

// Three ways to create virtual threads in Java 21
public class VirtualThreadBasics {
    public static void main(String[] args) throws Exception {
        // 1. Thread.ofVirtual()
        Thread vt1 = Thread.ofVirtual()
            .name("my-vthread")
            .start(() -> System.out.println("Running in: " + Thread.currentThread()));
        vt1.join();

        // 2. Thread.startVirtualThread()
        Thread vt2 = Thread.startVirtualThread(() ->
            System.out.println("Virtual: " + Thread.currentThread().isVirtual())
        );
        vt2.join();

        // 3. Executor (preferred for pools)
        try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
            exec.submit(() -> System.out.println("Via executor"));
        }
        
        System.out.println("Is virtual: " + vt1.isVirtual());
        System.out.println("Is daemon:  " + vt1.isDaemon()); // always true
    }
}

💡 Virtual threads are always daemon threads and do not prevent JVM shutdown.


Step 2: Carrier Threads and Mounting/Unmounting

💡 Pinning (virtual thread stuck to carrier) happens with synchronized blocks. Use ReentrantLock in new code.


Step 3: 10000 Virtual Threads Benchmark

📸 Verified Output:

💡 The advantage grows dramatically with higher I/O wait times (e.g., 100ms sleep → virtual threads 10x faster with 200 platform threads). At 1ms sleep with 200 threads the difference is minimal — increase the sleep or reduce pool size to see the gap.


Step 4: Thread Locals vs ScopedValues

ThreadLocal vs ScopedValue comparison:

Feature

ThreadLocal<T>

ScopedValue<T>

Mutability

Mutable

Immutable in scope

Memory leak risk

Yes (via thread pool)

No (scope-bound)

Virtual thread safe

Requires care

Designed for Loom

Inheritance

opt-in

Automatic to subtasks

API

set()/get()/remove()

where().run()


Step 5: Structured Concurrency Pattern (Manual)

⚠️ StructuredTaskScope is preview in Java 21 — use manual equivalents:


Step 6: Virtual Thread Pinning and Diagnostics


Step 7: Virtual Threads with HTTP (Conceptual)


Step 8: Capstone — Full Virtual Thread Demo

📸 Verified Output:


Summary

Concept
API
Key Benefit

Virtual thread

Thread.ofVirtual()

Lightweight, JVM-scheduled

Executor

newVirtualThreadPerTaskExecutor()

One thread per task, scalable

Mounting/unmounting

Automatic on block

Carrier threads stay busy

Pinning

Avoid synchronized + I/O

Use ReentrantLock

ScopedValue

ScopedValue.where().run()

Safe context propagation

Structured concurrency

Manual Future pattern

Lifecycle-bound subtasks

JFR diagnostics

jdk.VirtualThreadPinned

Pinning detection

Last updated