Concepts · Java fundamentals

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

ToolGives you
synchronizedmutual exclusion + visibility; re-entrant
volatilevisibility and ordering for one variable, not atomicity
AtomicInteger, LongAdderlock-free atomic updates (CAS)
ReentrantLocktryLock, timeouts, fairness, multiple Conditions
ReadWriteLockmany readers or one writer
ConcurrentHashMapthread-safe map; use compute/merge for atomic updates
CountDownLatch, CyclicBarrier, Semaphorewait for N events · sync N threads · limit concurrent access
CompletableFuturecompose 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/notify vs Condition: same idea; Condition works with Lock and allows several wait sets.
  • Thread-safe singleton: an enum, or a lazy holder class.
  • Executors.newFixedThreadPool uses an unbounded queue; under load prefer a ThreadPoolExecutor with a bounded queue and a rejection policy.