Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

The concurrency toolkit

synchronized, locks, volatile, atomics, concurrent collections, executors, CompletableFuture, virtual threads and structured concurrency.

Two separate questions.

How do threads share data safely?

  • Best: don't share. Confine data to one thread, or share only immutable objects.
  • One counter or flag → an atomic variable (AtomicInteger, LongAdder).
  • A shared map → ConcurrentHashMap and its atomic methods (merge, computeIfAbsent).
  • Several fields that must change together → synchronized or a ReentrantLock, always taking several locks in the same order.
  • A flag one thread writes and others only read → volatile.

How do you run work concurrently?

  • A batch of tasks → an ExecutorService; for blocking I/O, one virtual thread per task.
  • A chain of asynchronous steps → CompletableFuture.
  • Subtasks that must succeed or fail together → structured concurrency.

The problems

A bug where a program's result depends on how the scheduler happens to interleave its threads.

Example

count++ from two threads loses increments.

Two threads' steps interleave and an update is lost.

Section

Race conditions

Read-modify-write, check-then-act, and how to find them.

Threads each waiting for a lock another of them holds, so none can ever proceed.

Each thread holds a lock the other needs.

Section

Deadlock

Lock ordering and timeouts.

Sharing data safely

Mutual exclusion and visibility.

A one-value holder, such as AtomicInteger, whose updates happen in one indivisible step.

One variable updated atomically without a lock.

A thread-safe map with atomic per-key operations such as merge and computeIfAbsent.

A map many threads can update at once.

Lock-free counters and thread-safe collections.

Running work

An object you hand tasks to, which runs them on threads it manages and returns Futures.

A pool that runs submitted tasks and hands back futures.

Submitting tasks and collecting results.

Chaining asynchronous steps.

A cheap thread managed by the JVM that frees its carrier while it waits.

A cheap thread for blocking work; millions can exist.

Subtasks that start, fail and finish together.