Java Concurrency: volatile vs synchronized vs Atomic vs Locks (2025 Guide)

 

Java Concurrency: volatile vs synchronized vs Atomic vs Locks (2025 Guide)

A complete comparison for backend engineers & interviews.

Java provides multiple tools to handle concurrency:

volatile
synchronized
java.util.concurrent.atomic (e.g., AtomicInteger)
Locks (e.g., ReentrantLock)

Each solves different problems.


1. Summary Comparison Table

FeaturevolatilesynchronizedAtomic ClassesLocks (ReentrantLock)
Visibility✅ Yes✅ Yes✅ Yes✅ Yes
Atomicity❌ No✅ Yes✅ Yes (for built-in ops)✅ Yes
Prevents Reordering✅ Yes✅ Yes✅ Yes✅ Yes
Allows Multiple Threads?✅ Yes✅ Only inside critical section✅ Yes✅ Yes
Performance⭐ Very Fast⚠️ Slower (blocking)⭐ Fast (non-blocking)⭐ Fast (flexible, non-blocking options)
Blocking❌ No✅ Yes❌ No⚠️ Optional locking
Use CaseState flags, simple booleansMulti-step operationsCounters, accumulatorsComplex concurrency control

2. What Each One Actually Does

A. volatile

What it is:
A lightweight keyword ensuring visibility and ordering, but not atomicity.

Use when:

  • Shared flags

  • Configuration states

  • “Stop thread” signals

  • Values written by one thread and read by many threads

  • No dependency on old value

Not good for:
❌ counters (i++), ❌ collections, ❌ multi-step updates


B. synchronized

What it is:
A monitor lock that ensures:

✅ Only one thread can enter a block/method
✅ Atomicity
✅ Visibility
✅ Ordering

Use when:

  • Multi-step operations

  • Updating shared objects

  • Consistency required across multiple variables

Drawbacks:
⚠️ Blocking → thread waits
⚠️ Can cause contention
⚠️ Can lead to deadlock if misused


C. Atomic Classes (AtomicInteger, AtomicLong, AtomicReference…)

What they are:
Non-blocking, lock-free concurrency utilities using CAS (Compare-And-Set) operations.

Use when:

  • High-performance counters

  • Increment/decrement

  • Accumulators

  • Single-variable atomic operations

Benefits:
✅ Faster than synchronized
✅ No blocking
✅ Thread-safe increments

Limitations:
❌ Not good for multi-step logic
❌ Limited to atomic ops on one variable


D. Locks (ReentrantLock, ReentrantReadWriteLock…)

What they are:
Advanced locking primitives offering more control than synchronized.

Use when:

  • Need timed lock attempts

  • Need interruptible locks

  • Need fairness policies

  • Need read/write locks

  • Need conditions (like wait/notify but better)

Benefits:
✅ More flexible
✅ Can try to acquire lock without waiting
✅ ReadWriteLock allows multiple readers

Drawbacks:
⚠️ Must manually unlock (error-prone)
⚠️ More complex to write
⚠️ Slight overhead vs synchronized


3. Code Examples for Each


volatile Example — simple visibility

volatile boolean running = true; public void stop() { running = false; } public void run() { while (running) { // work } }

✅ Safe because it's a simple boolean
❌ Not safe for increments


synchronized Example — atomic multi-step

private int count = 0; public synchronized void increment() { count++; // atomic inside synchronized }

✅ Guarantees atomicity
✅ Prevents race conditions


AtomicInteger Example — high performance counter

AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); }

✅ Non-blocking
✅ Fast
✅ Thread-safe


ReentrantLock Example — flexible locking

ReentrantLock lock = new ReentrantLock(); public void update() { lock.lock(); try { // critical section } finally { lock.unlock(); } }

✅ Supports tryLock(), fairness, conditions
✅ Good for complex concurrency


4. When to Use What (Interview Gold Answer)

ScenarioBest ChoiceWhy
Boolean flags, configuration, “stop threads”volatileLightweight & visible
Multi-step updatessynchronizedAtomic + blocking
High-performance counters (increment/decrement)AtomicIntegerNon-blocking, fast
Multiple readers, one writerReadWriteLockBetter concurrency
Complex lock control (timeout, interrupt, fairness)ReentrantLockMore flexible
Protecting compound operations across many fieldssynchronized or ReentrantLockCoordinated atomicity
Replacing synchronized for performanceAtomic* or ReentrantLockLock-free, fine-grained

5. Memory Model Diagram (Visibility Problem)

Without volatile or synchronization: Thread A updates (CPU cache) Thread B reads stale value (old cache) With volatile / synchronized / lock: Thread A writes --> main memory Thread B reads <-- latest value

6. Simple Decision Tree

Do you only need visibility? → volatile Do you need atomic multi-step? → synchronized or Lock Do you need fast atomic increment? → AtomicInteger Do you need advanced lock features? → ReentrantLock Do you have many readers, few writers? → ReadWriteLock

No comments:

Post a Comment

Model Context Protocol (MCP) — Complete Guide for Backend Engineers

  Model Context Protocol (MCP) — Complete Guide for Backend Engineers Build Tools, Resources, and AI-Driven Services Using LangChain Moder...

Featured Posts