Skip to main content

Thread Safety and Lazy Init

Source: Effective Java, 3rd Edition — Chapter 11 by Joshua Bloch (Items 82–84)

This lesson closes Chapter 11: documenting thread safety, lazy initialization idioms, and why programs must not depend on the thread scheduler.

Item 82: Document thread safety​

Every class should document how it behaves under concurrent use. synchronized in source is not documentation — Javadoc omits it, and thread safety is not all-or-nothing.

LevelMeaningExamples
ImmutableNo sync neededString, BigInteger
Unconditionally thread-safeInternal sync covers all useAtomicLong, ConcurrentHashMap
Conditionally thread-safeSome sequences need external syncCollections.synchronizedMap iterators
Not thread-safeClient must sync each useArrayList, HashMap
Thread-hostileBroken even with client syncUnsynchronized static mutation

Conditionally thread-safe types must say which sequences need external sync and which lock (often the map itself, not a view).

For unconditionally thread-safe classes, prefer a private final Object lock over synchronized methods — clients can’t hold your public lock (denial-of-service) and subclasses can’t accidentally share the monitor with you.

Quiz

Document thread safety

1 / 2

Class Javadoc lists synchronized on each method — thread-safe?

Pick an answer to see if you are right.

Item 83: Use lazy initialization judiciously​

Default: normal initialization with final fields. Lazy init is an optimization (or breaks init cycles) — measure before using it.

Thread-safe patterns:

CaseIdiom
Break init cyclesynchronized accessor
Lazy static field (performance)Initialization-on-holder
Lazy instance field (performance)Double-check with volatile
// Static — holder class
private static class Holder {
static final FieldType field = computeFieldValue();
}
static FieldType getField() { return Holder.field; }

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

Lazy initialization

1 / 2

Performance-motivated lazy static field in multithreaded code

Pick an answer to see if you are right.

Item 84: Don’t depend on the thread scheduler​

Correctness and portability must not rely on which thread runs when. Keep runnable threads near the processor count; threads should block waiting for work, not busy-wait.

Don’t “fix” liveness with Thread.yield() or thread priorities — behavior varies by JVM and platform. Size thread pools appropriately (Item 80); use CountDownLatch, BlockingQueue, etc., instead of spinning.

Quiz

Thread scheduler

1 / 2

Program barely works; adding Thread.yield() fixes it on your laptop

Pick an answer to see if you are right.

Items 82–84 map​

ItemMove
82Document thread-safety level; private lock when unconditional
83final eager init; holder (static) or double-check volatile (instance)
84Block, don’t spin; no yield/priority hacks

Chapter 11 complete. Next: Serialization (Chapter 12, Items 85–87).