Concurrency
Threads, synchronization, the memory model, executors and the concurrent utilities.
Threads
Create with a Runnable / Callable, but in real code submit tasks to an ExecutorService instead of starting threads by hand. States: NEW → RUNNABLE → BLOCKED / WAITING / TIMED_WAITING → TERMINATED.
Java 21 virtual threads (Executors.newVirtualThreadPerTaskExecutor()) are cheap enough to use one per blocking task.
The three problems
- Atomicity:
count++is read-modify-write; two threads can lose an update. - Visibility: a write in one thread may not be seen by another without a happens-before edge.
- Ordering: the compiler and CPU may reorder instructions.
Tools
| Tool | Gives you |
|---|---|
synchronized | mutual exclusion + visibility; re-entrant |
volatile | visibility and ordering for one variable, not atomicity |
AtomicInteger, LongAdder | lock-free atomic updates (CAS) |
ReentrantLock | tryLock, timeouts, fairness, multiple Conditions |
ReadWriteLock | many readers or one writer |
ConcurrentHashMap | thread-safe map; use compute/merge for atomic updates |
CountDownLatch, CyclicBarrier, Semaphore | wait for N events · sync N threads · limit concurrent access |
CompletableFuture | compose async steps without blocking |
Happens-before (the rules that matter)
Unlocking a monitor → a later lock of it. Writing a volatile → a later read of it. Thread.start() → everything the new thread does. Everything a thread does → another thread’s join() on it.
Deadlock
Needs four things at once: mutual exclusion, hold-and-wait, no preemption, circular wait. Break one, usually by always taking locks in the same global order, or with tryLock and a timeout.
Interview angles
wait/notifyvsCondition: same idea;Conditionworks withLockand allows several wait sets.- Thread-safe singleton: an enum, or a lazy holder class.
Executors.newFixedThreadPooluses an unbounded queue; under load prefer aThreadPoolExecutorwith a bounded queue and a rejection policy.