Chapter 15 of 16Foundation

Concurrency and Thread Safety

Understand visibility, atomicity, thread confinement, and executors before sharing work between threads.

30 minJava 21
01

Why it matters

Start from the concept, not the syntax

Meet a concrete situation where this idea earns its place, then state the boundary it guards.

Concurrency lets a program do several things at once—but shared mutable state turns timing into correctness. Threads and executors supply the machinery; synchronization discipline keeps the results deterministic.

By the end of this chapter

Unsynchronized shared state produces timing-dependent wrong totals that pass tests and fail in production.

  • Thread and Runnable
  • Race conditions
  • ExecutorService
02

Mechanism

How Java expresses the concept

Walk the actual types, rules, and runtime behavior involved—the same behavior described in the Java Language Specification.

The mental model

A `Thread` is a unit of execution. You can extend `Thread` or, better, implement `Runnable` and pass it to a thread; `Runnable` separates the task from the threading mechanism. Calling `start` runs the task concurrently; calling `run` directly just runs it on the current thread.

Shared mutable state is the source of bugs. When two threads read and write the same field without coordination, a race condition produces results that depend on timing. `synchronized` gives a lock so only one thread enters the block at a time; `volatile` makes a single variable visible across threads but is not a mutex.

Prefer an `ExecutorService` over hand-made threads: it pools threads, queues tasks, and lets you shut down cleanly. Submit `Runnable` or `Callable` (which returns a result and can throw). Always `shutdown` the pool so the program can exit.

Concurrency is hard to test because failures are intermittent. Design for it: share as little state as possible, prefer immutable values, and use higher-level tools (`java.util.concurrent`) before reaching for low-level locks.

03

Concept map

Inspect the building blocks as a whole

Use the map as a structural reference; it shows the pieces and their relationships, not an execution sequence.

Core Java

Threads and concurrency

Concurrency runs several tasks at once. Java gives you threads, the Runnable contract, and executors that manage pools for you.

  1. 01Thread and Runnable
  2. 02start vs run
  3. 03Race conditions
  4. 04synchronized and volatile
  5. 05ExecutorService
  6. 06Shut down pools
04

Implementation

Encode the smallest runnable example

Write and run the code, then observe the behavior you just predicted with a real Java program.

Runnable snippet path

Predict, run, and change each example

2 focused examples
01

Run a task on a thread

Implement Runnable and hand it to a Thread, then start it.

GreetTask.java
public class GreetTask implements Runnable {
    public void run() {
        System.out.println("working");
    }

    public static void main(String[] args) {
        new Thread(new GreetTask()).start();
    }
}

Remember

Call start, not run, or the task runs on the calling thread.

Change one input, predict the result, then compile or test it before continuing.

02

Guard shared state

synchronized ensures only one thread at a time changes the counter.

Counter.java
public class Counter {
    private int value;
    synchronized void increment() { value++; }
    synchronized int value() { return value; }
}

Remember

Every access to shared mutable state must have a safe visibility and atomicity strategy: immutability, thread confinement, volatile fields, atomics, or locks may be appropriate depending on the invariant.

Change one input, predict the result, then compile or test it before continuing.

05

Watch out

Break it on purpose, then repair the right layer

Predict the symptom before running the failure case, and close with the completion checklist.

Deliberate failure

  1. Break

    Increment a shared counter from two threads without any synchronization

  2. Observe

    The final count is occasionally less than the number of increments

  3. Repair

    Coordinate every access to shared state with synchronized or a higher-level concurrent tool

Why it matters

Coordinate every access to shared state with synchronized or a higher-level concurrent tool

Completion checklist

  • Run work with Thread or Runnable
  • Explain and prevent a race condition
  • Submit tasks to an ExecutorService and shut it down
06

Go deeper

Official references

The primary sources behind this chapter. Every claim above stays consistent with them.