Real World Example

Notification Fan-out (Gmail / Swiggy style)

When a sale starts, an app may send push notifications to thousands of users. Each send is a slow HTTP call to a push service. With virtual threads each send is simple blocking code, and a Semaphore respects the provider's limit of concurrent calls.

Example

5,000 notifications, at most 500 in flight, each call takes 200 ms.

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class Main {
    static final Semaphore PROVIDER_LIMIT = new Semaphore(500);
    static final AtomicInteger sent = new AtomicInteger();
    static final AtomicInteger failed = new AtomicInteger();

    static void sendPush(int userId) throws InterruptedException {
        PROVIDER_LIMIT.acquire();
        try {
            Thread.sleep(200);                   // HTTP call to the push provider
            if (userId % 1000 == 999) throw new IllegalStateException("invalid device token");
            sent.incrementAndGet();
        } catch (IllegalStateException e) {
            failed.incrementAndGet();
        } finally {
            PROVIDER_LIMIT.release();
        }
    }

    public static void main(String[] args) {
        long start = System.nanoTime();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int user = 0; user < 5_000; user++) {
                final int id = user;
                executor.submit(() -> { sendPush(id); return null; });
            }
        }
        long ms = (System.nanoTime() - start) / 1_000_000;
        System.out.println("Sent: " + sent.get() + ", failed: " + failed.get());
        System.out.println("About 2 seconds (10 waves of 500 x 200 ms), not 1000 s: " + (ms < 6000));
    }
}

Output

Sent: 4995, failed: 5
About 2 seconds (10 waves of 500 x 200 ms), not 1000 s: true