Multithreading and Concurrency Questions

How do you create a thread in Java?

  1. Extend Thread and override run().
  2. Implement Runnable (preferred, the class can still extend something else) and pass it to new Thread(r).
  3. Implement Callable<V> and submit it to an ExecutorService to get a Future with a result.
  4. Java 21: Thread.ofVirtual().start(r) or Executors.newVirtualThreadPerTaskExecutor().

Note: Call start(), not run(). run() just executes the method on the current thread.

What are the states of a thread?

NEW ──start()──▶ RUNNABLE ──run() ends──▶ TERMINATED
                    │   ▲
     waiting for a  │   │  lock acquired / notified / time over
   lock, wait(),    ▼   │
   sleep(), join()  BLOCKED / WAITING / TIMED_WAITING

What is synchronization and why is it needed?

When threads share mutable data, operations like count++ (read, add, write) can interleave and lose updates (a race condition). synchronized lets only one thread at a time hold an object's monitor lock, and it also guarantees visibility of changes.

Example

Two threads incrementing 10,000 times each with a synchronized method always give 20,000.

public class Main {
    private int count = 0;

    synchronized void increment() { count++; }

    public static void main(String[] args) throws InterruptedException {
        Main counter = new Main();
        Runnable task = () -> {
            for (int i = 0; i < 10_000; i++) counter.increment();
        };
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println("Count = " + counter.count);
    }
}

Output

Count = 20000

What does volatile do? Is it enough for count++?

volatile guarantees visibility: every read sees the latest write, and it prevents reordering around the variable. It does not make compound actions atomic, so volatile int count; count++ is still unsafe. Use it for flags (volatile boolean running); use AtomicInteger or locks for counters.

sleep() vs wait()?

sleep()wait()
ClassThread (static)Object
Releases locknoyes
Called fromanywhereinside synchronized on the same object
Wakes upafter the timenotify()/notifyAll() or timeout

What is a deadlock and how do you prevent it?

A deadlock happens when two or more threads each hold a lock and wait forever for a lock held by the other. Example: T1 locks A then wants B, T2 locks B then wants A.

  • Always acquire locks in the same global order.
  • Use ReentrantLock.tryLock(timeout) instead of blocking forever.
  • Keep synchronized blocks small and avoid calling unknown code while holding a lock.
  • Detect with a thread dump (jstack <pid>), which reports "Found one Java-level deadlock".

What is ExecutorService and why use a thread pool?

Creating a thread per task is expensive and unbounded. An ExecutorService reuses a fixed set of worker threads and queues tasks. Common factories: newFixedThreadPool(n), newCachedThreadPool(), newSingleThreadExecutor(), newScheduledThreadPool(n). Always call shutdown().

Example

Submitting Callables and reading Futures in order.

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

public class Main {
    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        List<Future<Integer>> results = new ArrayList<>();
        for (int i = 1; i <= 4; i++) {
            int n = i;
            results.add(pool.submit(() -> n * n));
        }
        for (Future<Integer> f : results) {
            System.out.println("Result: " + f.get());
        }
        pool.shutdown();
    }
}

Output

Result: 1
Result: 4
Result: 9
Result: 16

Runnable vs Callable?

Runnable.run() returns nothing and cannot throw checked exceptions. Callable.call() returns a value and can throw checked exceptions. Submitting a Callable to an executor returns a Future<V> whose get() waits for the result.

What is CompletableFuture?

A Future you can compose without blocking: supplyAsync, then thenApply / thenCompose / thenCombine, and handle errors with exceptionally / handle. allOf waits for many tasks. Used to call several services in parallel.

Example

Two "service calls" run in parallel and are combined.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        CompletableFuture<String> user = CompletableFuture.supplyAsync(() -> "Ravi");
        CompletableFuture<Integer> orders = CompletableFuture.supplyAsync(() -> 7);

        String summary = user
            .thenCombine(orders, (u, o) -> u + " has " + o + " orders")
            .exceptionally(ex -> "fallback")
            .join();
        System.out.println(summary);
    }
}

Output

Ravi has 7 orders

synchronized vs ReentrantLock?

synchronizedReentrantLock
Unlockautomatic at block endmanual unlock() in finally
tryLock / timeoutnoyes
Interruptible waitnolockInterruptibly()
Fairnessnooptional fair mode
Conditionsone (wait/notify)many Condition objects

What are virtual threads (Java 21)?

Lightweight threads managed by the JVM, not the OS. Millions can exist; when one blocks on I/O, the JVM unmounts it from its carrier (platform) thread so the carrier serves others. They make simple blocking "thread-per-request" code scale like async code. Create with Thread.ofVirtual().start(...) or Executors.newVirtualThreadPerTaskExecutor(). Do not pool them; they help I/O-bound, not CPU-bound, work.