Mastering Java Thread Safety: Levels, Strategies, and Best Practices
This article explains Java thread‑safety concepts, classifies five safety levels from immutable to thread‑hostile, and details synchronization techniques—including mutual exclusion, non‑blocking CAS, and no‑sync approaches like reentrant code and thread‑local storage—to help developers write safe concurrent code.
Thread safety is a frequent interview topic and a practical concern in real projects; ensuring safety is essential.
Thread Safety Levels
Thread safety is not a binary property; Java operations can be grouped into five categories from strongest to weakest: immutable, absolutely thread‑safe, relatively thread‑safe, thread‑compatible, and thread‑hostile.
1. Immutable
Immutable objects are inherently thread‑safe; for example, data declared with the final keyword cannot be modified.
2. Absolutely Thread‑Safe
Absolute thread safety meets Brian Goetz’s strict definition: a class works correctly without any external synchronization regardless of the runtime environment, often at high cost.
3. Relatively Thread‑Safe
This is the usual meaning of “thread‑safe”. Individual operations are safe without extra measures, but certain sequences may require external synchronization. Java classes like Vector , Hashtable , and Collections.synchronizedCollection() belong here.
4. Thread‑Compatible
These classes are not thread‑safe by themselves but can be used safely if callers apply proper synchronization, such as ArrayList and HashMap .
5. Thread‑Hostile
Code that cannot be made safe even with synchronization, e.g., the deprecated Thread.suspend() and Thread.resume() methods, which can cause deadlocks.
How to Achieve Thread Safety
Thread safety can be achieved either with synchronization or without it.
1. Mutual Exclusion Synchronization
Mutual exclusion ensures that only one thread accesses shared data at a time. In Java, the synchronized keyword and ReentrantLock provide this mechanism, but they introduce blocking and performance overhead.
2. Non‑Blocking Synchronization
Optimistic concurrency uses techniques like Compare‑And‑Swap (CAS). CAS atomically updates a value when it matches an expected old value, but suffers from the ABA problem, which can be mitigated with version stamps (e.g., AtomicStampedReference ).
CAS Drawbacks
The ABA problem occurs when a value changes from A to B and back to A, making a CAS check think nothing changed; using a version number solves this.
3. No‑Synchronization Approaches
Some code is inherently thread‑safe and does not require synchronization.
Reentrant Code
Reentrant (pure) code does not rely on shared mutable state and can be interrupted and resumed safely; all reentrant code is thread‑safe, though not all thread‑safe code is reentrant.
Thread‑Local Storage
Limiting data visibility to a single thread, such as using thread‑local variables or designs like the “thread‑per‑request” model, avoids contention without explicit locks.
Sanyou's Java Diary
Passionate about technology, though not great at solving problems; eager to share, never tire of learning!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.