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 synchronized for atomicity and volatile for visibility.
  • ExecutorService: Never create threads manually. Use FixedThreadPool for stable workloads and CachedThreadPool for 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

  1. Race Condition: Two threads writing to memory at the same time = data corruption.
  2. Deadlock: Thread A holds Lock 1, waits for Lock 2. Thread B holds Lock 2, waits for Lock 1.
  3. Livelock: Threads are active but making no progress (e.g., politely yielding to each other forever).
  4. Starvation: High-priority threads hog resources; low-priority threads never run.
  5. 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.

System Design Glossary