Concurrency

Concurrency Doctrine (EJ 78–84 + JCiP)

The distilled rules: synchronize all access or none, keep locks small and private, prefer executors and utilities to threads and wait/notify, document thread safety honestly, and treat lazy initialization as a last resort.
  • Synchronize access to shared mutable data — reads included (78)
  • Avoid excessive synchronization: no alien calls under a lock (79)
  • Executors/tasks over threads (80); utilities over wait/notify (81)
  • Document thread safety: immutable / unconditionally safe / conditionally / not / hostile (82)
  • Lazy initialization judiciously: holder idiom for statics, volatile DCL for instances (83)
  • Never depend on the thread scheduler, priorities, or Thread.yield (84)
Thread-safety levels (EJ Item 82)
LevelMeaningExamples
Immutableconstant; no external synchronization everString, Long, BigInteger, records of immutables
Unconditionally thread-safeinternally synchronized, use freelyAtomicLong, ConcurrentHashMap
Conditionally thread-safesome sequences need client lockingCollections.synchronizedMap iteration
Not thread-safecallers must synchronize every accessArrayList, HashMap
Thread-hostileunsafe even fully synchronized (static mutable abuse)rare; usually a bug

Documentation is part of the API: callers can't see your synchronization policy from signatures. State the level; for conditional safety, state exactly which sequences need which lock (the synchronizedMap javadoc pattern: iterate while holding the map). Undocumented assumptions become other people's data races.

Lazy init cheat sheet (EJ Item 83)
// Almost always best: don't be lazy.
private final FieldType field = computeFieldValue();

// Lazy static — the holder idiom (JVM does the synchronization):
private static class Holder { static final FieldType FIELD = computeFieldValue(); }
static FieldType getField() { return Holder.FIELD; }

// Lazy instance — volatile double-check:
private volatile FieldType field;
FieldType getField() {
    FieldType result = field;
    if (result == null) {
        synchronized (this) {
            if (field == null) field = result = computeFieldValue();
        }
    }
    return result;
}

JCiP's closing wisdom compresses further: less mutable state (every removed field is removed analysis), less sharing (confinement is free safety), more immutability, and when synchronization is unavoidable, make the policy boring — one obvious lock per invariant, documented, held briefly. Clever concurrency is a maintenance liability; simple concurrency survives refactoring.

Sources
  • Effective Java (3rd ed.)Items 78–84
  • Java Concurrency in PracticeCh. 12, 16 — Testing; The JMM