Multithreading is a fundamental skill for any serious Java developer. Whether you're optimizing performance, improving responsiveness, or handling thousands of concurrent usersβunderstanding multithreading is essential.
This cheat sheet provides a quick-reference guide packed with syntax, concepts, tools, patterns, and expert tips to write robust concurrent code in Java.
π§΅ Core Concepts of Java Multithreading
| Concept | Summary |
|---|---|
| Thread | Lightweight unit of execution |
| Runnable/Callable | Task abstraction for threading |
| Concurrency | Multiple tasks in overlapping time |
| Parallelism | Multiple tasks running simultaneously |
| Shared State | Multiple threads accessing same memory |
| Race Condition | Incorrect behavior from unsynchronized access |
| Deadlock | Threads waiting on each other forever |
π Thread Lifecycle States
NEW β RUNNABLE β BLOCKED β WAITING β TERMINATED
NEW: Created but not startedRUNNABLE: Ready to run or runningBLOCKED: Waiting for lockWAITING: Waits indefinitelyTIMED_WAITING: Waits with timeoutTERMINATED: Done
π¦ Thread Creation Syntax
Traditional
Thread t = new Thread(() -> doWork());
t.start();
ExecutorService
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> doWork());
pool.shutdown();
Virtual Threads (Java 21+)
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
exec.submit(() -> doWork());
}
π§ Java Memory Model Tips
- Use
volatilefor visibility across threads synchronizedensures mutual exclusion and visibility- Use atomic classes (
AtomicInteger,AtomicReference) for lock-free updates
π Locking & Coordination Tools
| Tool | Use |
|---|---|
synchronized |
Basic lock and memory visibility |
ReentrantLock |
Lock with more control (timeouts, fairness) |
ReadWriteLock |
Separate read/write locking |
StampedLock |
Optimized read/write locking |
wait()/notify() |
Coordination using intrinsic lock |
join() |
Wait for thread to finish |
sleep() |
Pause thread |
CountDownLatch |
Wait until count reaches zero |
Semaphore |
Control concurrent access |
CyclicBarrier |
Wait until all parties reach a point |
π High-Level Concurrency Utilities
| Class | Use |
|---|---|
ExecutorService |
Thread pooling |
ForkJoinPool |
Recursive parallelism |
CompletableFuture |
Async result handling |
BlockingQueue |
Producer-consumer |
ConcurrentHashMap |
Thread-safe map |
CopyOnWriteArrayList |
Thread-safe list |
Phaser |
Dynamic synchronization barrier |
π Common Use Cases
β Producer-Consumer
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
β Parallel File Processing
Use ExecutorService or ForkJoinPool to split and process.
β Asynchronous Tasks
CompletableFuture.supplyAsync(() -> compute())
.thenApply(result -> transform(result));
π οΈ Debugging & Monitoring Tools
| Tool | Purpose |
|---|---|
jstack |
Thread dumps |
JVisualVM |
Thread inspector |
Java Mission Control (JMC) |
Lock contention & thread monitoring |
| IntelliJ Debugger | Multithreaded debugging |
JCStress |
Memory model stress tests |
JFR |
Java Flight Recorder for tracing |
π Design Patterns for Concurrency
| Pattern | Usage |
|---|---|
| Worker Thread | Reuse fixed number of threads |
| Future Task | Execute with result |
| Thread-per-message | New thread per request |
| Producer-Consumer | Shared queue between producers/consumers |
| Reactor | Event-driven I/O |
| Balking | Skip repeated work under condition |
| Thread Pool | Bounded threads to prevent resource exhaustion |
π« Anti-Patterns to Avoid
- Creating unbounded threads (
new Thread()in loops) - Logging inside
synchronizedblocks Thread.sleep()for timing coordination- Ignoring executor shutdown
- Accessing shared mutable state unsafely
β Golden Rules for Safe Multithreading
- Prefer
ExecutorServiceover raw threads - Always shut down thread pools
- Avoid shared mutable state; if needed, use locks or atomic variables
- Use
volatilecarefullyβonly for visibility, not atomicity - Use structured logging and thread naming for observability
π What's New in Java Versions?
Java 8
- Lambdas with
Runnable,Callable CompletableFutureparallelStream()
Java 9
Flow API(Reactive Streams)
Java 11
CompletableFuture.delayedExecutor()
Java 21
- β
Virtual Threads via
Executors.newVirtualThreadPerTaskExecutor() - β Structured Concurrency
- β Scoped Values
π§ Expert-Level FAQ
Q1: Whatβs the difference between a thread and a task?
A task is a unit of work; a thread is the vehicle to execute it.
Q2: How is volatile different from synchronized?
volatile ensures visibility; synchronized ensures both visibility and mutual exclusion.
Q3: Can I mix ForkJoin and Executors?
Yes, but be cautious about thread exhaustion and resource contention.
Q4: When should I use virtual threads?
For lightweight, high-concurrency, I/O-heavy tasks (e.g., HTTP calls).
Q5: What causes deadlocks?
Circular wait between threads holding different locks.
Q6: Are thread priorities reliable?
No. They are advisory and not enforced across all OSes.
Q7: What is false sharing?
Performance issue when multiple threads update variables on the same cache line.
Q8: How do I handle uncaught thread exceptions?
Thread.setDefaultUncaughtExceptionHandler((t, e) -> log(e));
Q9: Why use CompletableFuture over Future?
Supports chaining, exception handling, and async composition.
Q10: Is ThreadLocal safe?
Yes, for isolating data per thread. Avoid memory leaks by cleaning up.
π― Conclusion and Key Takeaways
This cheat sheet is your go-to reference for writing concurrent Java code:
- Master the syntax and semantics of threads, executors, and locks
- Know your toolsβdebuggers, profilers, analyzers
- Embrace modern constructs like
CompletableFuture,ForkJoinPool, and virtual threads - Avoid common concurrency bugs with best practices and patterns