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.