javaadvanced

Virtual Threads — Lightweight Concurrency

Use Java 21 virtual threads for massive concurrency without thread pool tuning or reactive frameworks.

java
import java.net.URI;
import java.net.http.*;
import java.time.*;
import java.util.concurrent.*;
import java.util.stream.*;
import java.util.List;

public class VirtualThreadDemo {

    // 1. Start a virtual thread
    public static void basic() throws InterruptedException {
        Thread vt = Thread.ofVirtual().name("worker").start(() -> {
            System.out.println(Thread.currentThread());
        });
        vt.join();
    }

    // 2. ExecutorService with virtual threads
    public static void withExecutor() throws Exception {
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // Submit 10,000 concurrent tasks — no pool sizing needed
            List<Future<String>> futures = IntStream.range(0, 10_000)
                .mapToObj(i -> executor.submit(() -> fetchData(i)))
                .toList();

            for (var future : futures) {
                System.out.println(future.get());
            }
        }
    }

    // 3. Structured concurrency (preview)
    public static void structured() throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Subtask<String> user = scope.fork(() -> fetchData(1));
            Subtask<String> orders = scope.fork(() -> fetchData(2));
            scope.join().throwIfFailed();
            System.out.println(user.get() + " | " + orders.get());
        }
    }

    // 4. HTTP calls — 1000 concurrent with virtual threads
    public static void httpParallel() throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        Instant start = Instant.now();

        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<Integer>> results = IntStream.range(0, 1000)
                .mapToObj(i -> executor.submit(() -> {
                    HttpRequest req = HttpRequest.newBuilder()
                        .uri(URI.create("https://httpbin.org/get"))
                        .build();
                    return client.send(req, HttpResponse.BodyHandlers.ofString()).statusCode();
                }))
                .toList();

            long success = results.stream()
                .map(f -> { try { return f.get(); } catch (Exception e) { return 0; } })
                .filter(code -> code == 200)
                .count();

            System.out.printf("%d OK in %s%n", success, Duration.between(start, Instant.now()));
        }
    }

    static String fetchData(int id) {
        try { Thread.sleep(Duration.ofMillis(50)); } catch (InterruptedException e) {}
        return "Data-" + id;
    }

    public static void main(String[] args) throws Exception {
        basic();
        withExecutor();
    }
}

Use Cases

  • High-concurrency web servers handling thousands of requests
  • Parallel API calls without reactive complexity
  • Replacing thread pools with lightweight virtual threads

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.