Master Java Thread Safety: Levels, Synchronization Techniques, and No‑Sync Strategies
This article explains Java's five thread‑safety categories—from immutable to thread‑hostile—and details practical ways to achieve safety, including mutex synchronization, non‑blocking CAS techniques, and no‑sync approaches such as re‑entrant code and thread‑local storage.
Thread Safety Levels
Java classes can be classified into five levels of thread‑safety, ordered from strongest to weakest: immutable, absolute thread safety, relative thread safety, thread‑compatible, and thread‑hostile.
Immutable
Immutable objects cannot change after construction, so they are inherently thread‑safe. Declaring fields as final is a typical way to create immutable objects.
Absolute Thread Safety
A class is absolutely thread‑safe when it works correctly in any runtime environment without requiring callers to perform any additional synchronization. Achieving this usually incurs high implementation cost.
Relative Thread Safety
This is the common notion of a “thread‑safe” class: each individual operation is safe without external synchronization, but certain sequences of calls may still need external coordination.
Typical Java examples: Vector, Hashtable, and collections returned by Collections.synchronizedCollection().
Thread‑Compatible
Thread‑compatible classes are not thread‑safe by themselves, but become safe when callers synchronize correctly. Examples include ArrayList and HashMap.
Thread‑Hostile
Thread‑hostile code cannot be used safely even with synchronization. The deprecated methods Thread.suspend() and Thread.resume() are classic examples because they can cause deadlocks.
Methods to Achieve Thread Safety
1. Mutex Synchronization
Mutex synchronization guarantees exclusive access to shared data. In Java the synchronized keyword generates monitorenter and monitorexit bytecode instructions. The java.util.concurrent.locks.ReentrantLock provides a comparable re‑entrant lock with explicit lock() and unlock() methods.
2. Non‑Blocking Synchronization (CAS)
Optimistic concurrency uses hardware‑level conflict detection. The classic primitive is Compare‑And‑Swap (CAS), which atomically updates a memory location V from an expected value A to a new value B only if the current value equals A, returning the previous value.
CAS drawbacks
ABA problem: the value may change from A to B and back to A, making a simple equality check insufficient.
Solution: attach a version number or use java.util.concurrent.atomic.AtomicStampedReference, which stores a stamp together with the reference and checks both during compareAndSet.
3. No‑Sync Solutions
Code that does not share mutable state is inherently thread‑safe and does not require synchronization.
Re‑entrant (Pure) Code
Pure code can be interrupted at any point without affecting correctness because it does not rely on heap‑stored data or shared resources; all state is passed via method parameters.
Thread‑Local Storage
Limiting data visibility to a single thread eliminates contention. This pattern is used in producer‑consumer architectures and the “one request per thread” model employed by many web servers.
Senior Brother's Insights
A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.
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.
