Module Review: Concurrency Patterns
[!NOTE] This module explores the core principles of Module Review: Concurrency Patterns, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. Key Takeaways
- Concurrency vs. Parallelism: Concurrency is about dealing with many things at once (structure); parallelism is about doing many things at once (execution).
- Safety First: Shared mutable state is the root of all evil. Use
synchronizedfor atomicity andvolatilefor visibility. - ExecutorService: Never create threads manually. Use
FixedThreadPoolfor stable workloads andCachedThreadPoolfor bursty tasks. - Fork/Join Framework: Best for recursive, divide-and-conquer problems (like merge sort). It uses Work-Stealing to balance load.
- CompletableFuture: The modern way to handle async tasks. It allows non-blocking composition of pipelines (
thenApply,thenCompose). - Locking Hierarchy:
synchronized: Simple, reentrant, block-scoped.ReentrantLock: interruptible, supports fairness.ReadWriteLock: Allows multiple concurrent readers.StampedLock: Optimistic locking for high-performance reads.
2. Flashcards
What is the difference between `run()` and `start()`?
`start()` creates a new OS thread and calls `run()`. `run()` executes the code on the current thread (no parallelism).
Does `volatile` guarantee atomicity?
No. `volatile` only guarantees visibility (reading the latest value from RAM). It does not prevent race conditions like `count++`.
What is Work-Stealing?
An algorithm used by `ForkJoinPool` (and Go runtime) where idle threads take tasks from the tail of busy threads' queues.
When should you use `CompletableFuture` over `Future`?
Almost always. `Future.get()` is blocking. `CompletableFuture` allows non-blocking callback chaining (`thenApply`, `thenAccept`).
What is the Go equivalent of `synchronized`?
`sync.Mutex`. However, Go prefers sharing memory by communicating via Channels.
3. Cheat Sheet
| Component | Class / Keyword | Key Method | When to Use |
|---|---|---|---|
| Basic Lock | synchronized |
N/A | Simple mutual exclusion. |
| Advanced Lock | ReentrantLock |
.lock(), .tryLock() |
Need fairness or timeouts. |
| Read/Write | ReadWriteLock |
.readLock(), .writeLock() |
Many readers, few writers. |
| Thread Pool | ExecutorService |
.submit(), .shutdown() |
Managing thread lifecycles. |
| Async Result | CompletableFuture |
.supplyAsync(), .thenApply() |
Non-blocking pipelines. |
| Parallel Task | ForkJoinPool |
.invoke(), .fork() |
Recursive (Divide & Conquer). |
| Atomic | AtomicInteger |
.incrementAndGet() |
Lock-free counters. |
4. Quick Revision
- Race Condition: Two threads writing to memory at the same time = data corruption.
- Deadlock: Thread A holds Lock 1, waits for Lock 2. Thread B holds Lock 2, waits for Lock 1.
- Livelock: Threads are active but making no progress (e.g., politely yielding to each other forever).
- Starvation: High-priority threads hog resources; low-priority threads never run.
- Virtual Threads: Lightweight (user-mode) threads in Java 21+. Do not pool them!
5. Next Steps
Now that you’ve mastered concurrency, it’s time to ensure your code is robust and reliable.