Skip to content

Safety and Liveness

A safety property says that something bad never happens. A liveness property says that something good eventually happens.

Race conditions and data races

A race condition makes correctness depend on relative timing. A data race is a more specific unsynchronized conflicting access under a memory model, with at least one write. Race conditions can also occur in individually thread-safe operations composed into a non-atomic check-then-act sequence.

final class Counter {
    private long value;

    synchronized void increment() {
        value++;
    }

    synchronized long value() {
        return value;
    }
}

The same lock protects every access and establishes mutual exclusion plus visibility. For simple counters under contention, atomic or striped structures may fit better, but their compound-operation contracts still matter.

Liveness failures

  • deadlock: participants wait in a cycle that cannot resolve;
  • livelock: participants keep reacting but make no useful progress;
  • starvation: a participant is indefinitely denied service;
  • priority inversion: a high-priority participant waits on lower-priority work.

Deadlock conditions and prevention

Mutual exclusion, hold-and-wait, no preemption, and circular wait are necessary conditions in the classic reusable-resource model. Prevent deadlock by breaking at least one relevant condition: impose a global lock order, avoid holding locks during external calls, use timed acquisition, or redesign ownership.

Prefer confinement

Immutable data, thread confinement, message passing, and task-local state reduce the amount of synchronization needed. Concurrent collections provide specific atomic operations; several calls combined in client code are not automatically atomic.