Advanced Java
Multithreading
Complete lesson for Multithreading using LEARNCODEGURU’s 6-level learning model — from basics to advanced practice, with Java OOP and Collections where relevant.
Basics — Multithreading
Section: Advanced Java. Threads, Runnable, synchronized, volatile, executors, and ConcurrentHashMap.
What is a thread?
- A thread is a path of execution inside a process. The JVM starts with one thread:
main. - Create work with
Runnable(orCallable) and pass it to aThread, or better to anExecutorService. start()schedules a new thread. Callingrun()yourself just runs the code on the *current* thread — a classic interview trap.
public class ThreadBasics {
public static void main(String[] args) throws InterruptedException {
Runnable job = () -> System.out.println("hello from " + Thread.currentThread().getName());
Thread t = new Thread(job, "worker");
t.start();
t.join();
System.out.println("back to " + Thread.currentThread().getName());
}
} Thread vs Runnable vs Executor
| API | Role | Prefer when |
|---|---|---|
extends Thread | Thread + job coupled | Rarely — inflexible |
implements Runnable | Job only | Simple demos; pass to Thread/executor |
Callable | Job that returns / throws | Need a result via Future |
ExecutorService | Thread pool | Any real application |
synchronized and shared data
- Unsynchronized writes to shared fields race.
synchronizedmethods lock onthis(instance) or theClassobject (static). synchronized (lock) { }uses an explicit lock object — better than lockingthisif the instance is public.ConcurrentHashMapis a thread-safe map: concurrent reads/writes without locking the entire map. It does **not** allownullkeys or values (unlikeHashMap).
Advanced — Multithreading
Visibility, volatile, and the concurrent package
volatileguarantees visibility (and ordering) for a single field — not atomic++. UseAtomicIntegerfor counters.join()waits for a thread to finish.sleepdoes not release monitors;wait(on a monitor) does.- Prefer
java.util.concurrent:Executors.newFixedThreadPool,Future,CompletableFuture(Java 8),ConcurrentHashMap,BlockingQueue. - Do not call
stop()/suspend()/resume()— they are deprecated and unsafe. Use an interrupt flag orExecutorService.shutdown(). - Java 21 virtual threads change scaling, but interviews still start with platform threads,
synchronized, and concurrent collections.
Tricky Interview Points
Q1) start() vs run()?
Answer start() creates a new thread that later calls run(). run() invoked directly is a normal method call on the current thread.
Q2) Thread vs Runnable?
Answer Extending Thread wastes your one extends and mixes job with worker. Runnable (or Callable) separates the task; you can run it on any thread or pool.
Q3) What does synchronized do?
Answer It acquires a monitor, so only one thread executes that block for that lock, and it establishes a happens-before so writes become visible.
Q4) HashMap vs ConcurrentHashMap?
Answer HashMap is not thread-safe; concurrent mutation can corrupt it or loop. ConcurrentHashMap allows concurrent access, forbids nulls, and offers atomic compute/merge. Hashtable is the old fully-synchronized map — rarely used now.
Q5) What is volatile?
Answer A visibility/ordering modifier. Reads see the latest write. It does not make i++ atomic.
Q6) How do you wait for a worker to finish?
Answer thread.join(), or future.get(), or CountDownLatch/CyclicBarrier for groups.
Q7) Checked exception on run()?
Answer Runnable.run cannot throw checked exceptions. Use Callable.call or wrap in UncheckedIOException / complete a Future exceptionally.
Simple Programs
ExecutorService and ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ConcurrentCount {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 100; i++) {
pool.execute(() -> counts.merge("hits", 1, Integer::sum));
}
pool.shutdown();
pool.awaitTermination(2, TimeUnit.SECONDS);
System.out.println(counts.get("hits"));
}
} Problem Solving Tricks
- Always
shutdown()a pool (try-with-resources works forExecutorServicein Java 19+). - Name threads (
new Thread(r, "worker-1")) — stack dumps become readable. - Do not synchronize on a
Booleanor internedString. - For a simple flag,
volatile boolean runningplus interrupt is enough; do not invent a custom lock first. - Never use
HashMapas a shared cache without a lock — reach forConcurrentHashMap.
Practice Programs List
- Start two threads that print 1..5 and
joinboth frommain. - Show a race on
int counter++withoutsynchronized, then fix it withAtomicInteger. - Replace
extends Threadwith a lambdaRunnableon an executor. - Put 10,000
mergecalls on aConcurrentHashMapand check the total. - Demonstrate that calling
run()instead ofstart()keeps you onmain.