Java Concurrency Complete

Threads & Concurrency

Unlike single-threaded JavaScript, Java has real OS threads and true parallelism. That power comes with the classic hazards: race conditions, deadlocks, and shared mutable state.

Java 21 Interview Key 5 Topics
01

Threads & Runnable

A thread is an independent path of execution. The idiomatic way to define work is a Runnable (a functional interface), which you hand to a Thread to run in parallel with the rest of the program.

Java — Threads
// Runnable as a lambda — the preferred style
Runnable task = () -> System.out.println("running on " + Thread.currentThread().getName());

Thread t = new Thread(task);
t.start();          // starts a NEW thread — do not call run() directly
t.join();           // wait for t to finish before continuing

// Multiple threads run concurrently
for (int i = 0; i < 3; i++) {
    int id = i;
    new Thread(() -> System.out.println("worker " + id)).start();
}
Call start(), never run()run() just executes the body on the current thread with no parallelism. Only start() spins up a new thread.
02

ExecutorService — Thread Pools

Creating a raw Thread per task is wasteful. An ExecutorService manages a reusable pool of threads and a queue of work — the standard way to run many tasks concurrently.

Java — ExecutorService & Future
import java.util.concurrent.*;

ExecutorService pool = Executors.newFixedThreadPool(4);

// submit returns a Future — a handle to the eventual result
Future<Integer> future = pool.submit(() -> {
    return 2 + 2;
});

int result = future.get();     // blocks until the task finishes: 4

// run many tasks, wait for all
for (int i = 0; i < 10; i++) {
    int id = i;
    pool.submit(() -> System.out.println("job " + id));
}
pool.shutdown();          // stop accepting new work, finish queued tasks
Java 21 added virtual threads (Executors.newVirtualThreadPerTaskExecutor()) — lightweight threads where millions can run at once, making blocking I/O cheap without the async/await style JavaScript needs.
03

synchronized & Shared State

When two threads write the same variable, the result is a race condition. synchronized makes a method or block run one-thread-at-a-time on a given lock, so updates don't interleave.

Java — Race Condition & the Fix
class Counter {
    private int count = 0;

    // WITHOUT synchronized, count++ from two threads loses updates
    public synchronized void increment() {
        count++;              // now atomic per lock — no lost updates
    }
    public synchronized int get() { return count; }
}

// Or use an atomic — lock-free, often faster
import java.util.concurrent.atomic.*;
AtomicInteger safe = new AtomicInteger(0);
safe.incrementAndGet();      // thread-safe, no synchronized needed
04

Concurrent Collections

A plain HashMap shared across threads can corrupt itself. The java.util.concurrent package provides thread-safe collections built for exactly this.

Use instead ofThread-safe version
HashMapConcurrentHashMap
ArrayListCopyOnWriteArrayList
a shared queueBlockingQueue (e.g. LinkedBlockingQueue)
a counterAtomicInteger / AtomicLong
The old Collections.synchronizedMap() wraps every method in one big lock, so only one thread touches the map at a time. ConcurrentHashMap locks smaller segments, so many threads can work in parallel — prefer it.
05

Common Pitfalls

Calling run() instead of start()

thread.run() executes on the current thread with no concurrency at all. Only start() creates a new thread.

Race conditions on shared state

Two threads doing count++ without synchronization lose updates, because ++ is read-modify-write, not atomic. Use synchronized or an Atomic type.

Deadlock

Thread A holds lock 1 and waits for lock 2 while thread B holds lock 2 and waits for lock 1 — both block forever. Always acquire multiple locks in the same order.

Prefer higher-level tools

Reach for ExecutorService, ConcurrentHashMap, and Atomic types over raw threads and manual locks. They're correct by construction and far easier to reason about.

Quick Quiz

1. To run code on a new thread you call…
2. An ExecutorService gives you…
3. count++ from two unsynchronized threads can…
4. The thread-safe replacement for HashMap is…
5. Deadlock is avoided by…