iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Threads & Concurrency

Java threads are the foundation, but real concurrency code uses java.util.concurrent — ExecutorService for thread pools, CompletableFuture for async pipelines, locks/semaphores for coordination, and virtual threads (Java 21+) for cheap blocking I/O.

ExecutorService, CompletableFuture, virtual threads

EXAMPLE
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.time.Duration;
import java.util.List;

public class ThreadsDemo {

    // 1) Fixed thread pool — bounded, predictable
    static final ExecutorService POOL = Executors.newFixedThreadPool(8);

    static long blocking(long ms) throws InterruptedException {
        Thread.sleep(Duration.ofMillis(ms));
        return ms;
    }

    public static void main(String[] args) throws Exception {

        // 2) Submit + Future
        Future<Long> f = POOL.submit(() -> blocking(50));
        System.out.println("got " + f.get(1, TimeUnit.SECONDS));

        // 3) invokeAll — wait for many
        var tasks = List.<Callable<Long>>of(
            () -> blocking(30), () -> blocking(50), () -> blocking(20));
        List<Future<Long>> done = POOL.invokeAll(tasks);
        for (var fut : done) System.out.println(fut.get());

        // 4) CompletableFuture — async pipeline
        var stage = CompletableFuture
            .supplyAsync(() -> fetchData(), POOL)
            .thenApply(s -> s.toUpperCase())
            .thenCompose(s -> CompletableFuture.supplyAsync(() -> s.length(), POOL))
            .exceptionally(ex -> -1);

        System.out.println("length = " + stage.get());

        // 5) Combine many — allOf for fan-out / fan-in
        var f1 = CompletableFuture.supplyAsync(() -> "a", POOL);
        var f2 = CompletableFuture.supplyAsync(() -> "b", POOL);
        var f3 = CompletableFuture.supplyAsync(() -> "c", POOL);
        CompletableFuture.allOf(f1, f2, f3).join();
        System.out.println(f1.get() + f2.get() + f3.get());

        // 6) Atomic counters — lockless
        AtomicLong counter = new AtomicLong(0);
        for (int i = 0; i < 1000; i++) POOL.submit(() -> counter.incrementAndGet());

        // 7) Locks for multi-field state
        Object lock = new Object();
        synchronized (lock) {
            // critical section
        }
        // For more control: ReentrantLock, ReadWriteLock, StampedLock

        // 8) Virtual threads (Java 21+) — millions of cheap threads for blocking IO
        try (var vexec = Executors.newVirtualThreadPerTaskExecutor()) {
            var v = vexec.submit(() -> blocking(10));
            System.out.println("virtual got " + v.get());
        }

        // 9) Structured concurrency (preview in 21, stable in 22+)
        // try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        //   var a = scope.fork(() -> fetchUser(id));
        //   var b = scope.fork(() -> fetchOrders(id));
        //   scope.join().throwIfFailed();
        //   render(a.get(), b.get());
        // }

        // 10) Shutdown — never forget
        POOL.shutdown();
        if (!POOL.awaitTermination(10, TimeUnit.SECONDS)) {
            POOL.shutdownNow();
        }
    }

    static String fetchData() { return "hello"; }

    // ===== Pitfalls =====
    // - Forgetting executor.shutdown() -> threads keep the JVM alive
    // - Calling .get() without a timeout -> hangs forever on failure
    // - Sharing mutable state without sync -> data races
    // - Using newCachedThreadPool() unbounded -> can spawn thousands under load
    // - Doing CPU work on the common ForkJoinPool -> blocks parallel streams elsewhere
    // - Mixing virtual threads with synchronized blocks holding I/O -> pinning
}

Why it matters

For blocking I/O on Java 21+, use a virtual-thread executor (`Executors.newVirtualThreadPerTaskExecutor()`) and stop fighting thread pool sizes. Reserve fixed pools for CPU-bound work where the optimal pool size is "available cores"; let virtual threads handle the "thousands of waiting on a remote call" case the JVM was never great at before.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
new Thread(() -> System.out.println("in thread")).start();

var pool = Executors.newFixedThreadPool(4);
pool.submit(() -> work());
Try it Yourself »

Discussion

Loading…