Java · Advanced & Ecosystem

Multithreading

Concurrency lets programs do several things at once — handle many requests, download files in parallel, keep a UI responsive.

Advanced 5 min Lesson 25 of 31Examples target Java 21 LTS (they also run on Java 25).
0/31 in Java
On this page · 5 min · Advanced

In simple words#

A thread is a cook in a kitchen. More cooks finish orders faster — but if two cooks grab the same pan at once (shared data), the dish is ruined. Concurrency tools are the kitchen rules that prevent clashes.

Thread
A path of execution.
ExecutorService
A managed pool of threads.
Race condition
Wrong results when threads change shared data at the same time.

Learning objectives#

  • Run tasks in parallel with ExecutorService
  • Compose async work with CompletableFuture
  • Prevent race conditions
  • Use virtual threads

Explanation#

Don’t create raw threads for every task; submit tasks to an ExecutorService.

CompletableFuture chains async work: supplyAsync → thenApply → thenCombine → join.

Shared mutable data needs protection: AtomicInteger, ConcurrentHashMap, synchronized or locks. Better: avoid sharing.

Virtual threads (Java 21) are very lightweight — Executors.newVirtualThreadPerTaskExecutor() handles thousands of blocking tasks easily.

Code example: Parallel price checks with virtual threads#

JavaParallel price checks with virtual threads Illustrative
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;


public class Prices {
    static int fetchPrice(String shop) throws InterruptedException {
        Thread.sleep(500);                       // pretend this is an HTTP call
        return switch (shop) { case "ShopA" -> 54999; case "ShopB" -> 53999; default -> 55999; };
    }


    public static void main(String[] args) throws Exception {
        AtomicInteger calls = new AtomicInteger();
        long start = System.currentTimeMillis();


        try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<Integer>> results = pool.invokeAll(
                List.of("ShopA", "ShopB", "ShopC").stream()
                    .<Callable<Integer>>map(s -> () -> { calls.incrementAndGet(); return fetchPrice(s); })
                    .toList());


            int best = Integer.MAX_VALUE;
            for (Future<Integer> f : results) best = Math.min(best, f.get());
            System.out.println("Best price: ₹" + best + " from " + calls.get() + " shops");
        }
        System.out.println("Took ~" + (System.currentTimeMillis() - start) / 100 * 100 + " ms");
    }
}
Expected output
Best price: ₹53999 from 3 shops
Took ~500 ms

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. Each shop lookup is a Callable task.
  2. Virtual threads run all three at once.
  3. AtomicInteger counts calls safely across threads.
  4. Total time ≈ one call (500 ms), not three.

Real-world use case#

A travel site queries ten airline APIs in parallel and shows results in one second instead of ten.

Common mistakes#

Unsynchronised counters (count++)

count++ is not atomic. Use AtomicInteger or synchronisation.

Forgetting to shut down executors

Threads keep the app alive. Use try-with-resources (Java 19+) or shutdown().

Best practices#

  • Prefer executors over raw threads
  • Avoid shared mutable state
  • Use concurrent collections and atomics
  • Virtual threads for blocking I/O

Remember this#

Tasks → ExecutorService, not new Thread().

Shared data → atomic/concurrent/synchronized.

Java 21: virtual threads for many blocking tasks.

Practice exercise#

Your task: Run two async lookups and combine their results with CompletableFuture.

Show solution
Java Illustrative
var a = CompletableFuture.supplyAsync(() -> 54999);
var b = CompletableFuture.supplyAsync(() -> 53999);
int best = a.thenCombine(b, Math::min).join();

Mini quiz#

Multithreading quizMultiple choice Question 1 of 1

Which class provides a thread-safe counter?

Score so far: 0/0

Want more? Practise all Java MCQs · Java interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.