Java Notes
Complete guide to Synchronization in Java — race conditions, synchronized methods and blocks, intrinsic locks, volatile keyword, atomic variables, and deadlock prevention.
Why Synchronization is Needed
When multiple threads access shared mutable data simultaneously, race conditions occur — leading to corrupted data:
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Output (varies each run):
| Expected | 200,000 |
| Actual | 163,847 |
| Lost updates | 36,153 |
The synchronized Keyword
Synchronized Method
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Output:
Synchronized Block (Preferred — More Fine-Grained)
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Output:
| Starting balance | 10000 |
| Customer-1 withdrew 3000. Balance | 7000 |
| Customer-2 withdrew 3000. Balance | 4000 |
| Customer-3 withdrew 3000. Balance | 1000 |
| Customer-4 DENIED. Insufficient funds. Balance | 1000 |
| Customer-5 DENIED. Insufficient funds. Balance | 1000 |
| Final balance | 1000 |
How Intrinsic Locks (Monitors) Work
Thread A wants to enter synchronized block
┌────────────────────────────────────┐
│ synchronized(obj) { │
│ // critical section │←── Thread A ACQUIRES lock
│ } │ Thread B must WAIT
└────────────────────────────────────┘
Lock ownership
┌─────────────┐
│ OBJECT │
│ │
│ Lock: [A] │ ← Thread A holds the lock
│ │
│ Queue: [B] │ ← Thread B waiting to acquire
└─────────────┘
When A exits synchronized block
- Lock released
- B acquires lock and enters
Synchronized Static Methods
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
The volatile Keyword
volatile ensures visibility of changes across threads (but NOT atomicity):
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Deadlock
Deadlock occurs when two or more threads wait for each other's locks forever:
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Output:
Preventing Deadlock: Lock Ordering
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Atomic Variables (java.util.concurrent.atomic)
For simple counters, AtomicInteger is faster than synchronized:
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Common Mistakes
1. Synchronizing on wrong object
// ❌ BAD: Each thread synchronizes on a DIFFERENT lock
public void addItem(String item) {
synchronized (new Object()) { // New object each time!
list.add(item);
}
}
// ✅ GOOD: All threads use the SAME lock
private final Object lock = new Object();
public void addItem(String item) {
synchronized (lock) {
list.add(item);
}
}2. Over-synchronizing
// ❌ BAD: Entire method synchronized (blocks too long)
public synchronized void processOrder(Order order) {
validateOrder(order); // Doesn't need sync
calculateTotal(order); // Doesn't need sync
updateInventory(order); // Needs sync
sendConfirmation(order); // Doesn't need sync
}
// ✅ GOOD: Only synchronize what's necessary
public void processOrder(Order order) {
validateOrder(order);
calculateTotal(order);
synchronized (inventoryLock) {
updateInventory(order); // Only this needs sync
}
sendConfirmation(order);
}Interview Questions
Q1: What is a race condition?
Answer: A race condition occurs when two or more threads access shared mutable data concurrently, and the final result depends on the unpredictable timing of thread execution. It causes data corruption and inconsistent behavior.
Q2: What is the difference between synchronized method and synchronized block?
Answer: Synchronized method locks the entire method on this (or Class for static). Synchronized block is more granular — you choose which object to lock on and which code to protect. Block is preferred because it reduces lock contention.
Q3: What is a deadlock? How do you prevent it?
Answer: Deadlock: two or more threads permanently blocked waiting for each other's locks. Prevention: (1) consistent lock ordering, (2) lock timeout (tryLock), (3) avoid nested locks, (4) use concurrent utilities instead of manual locking.
Q4: What is the difference between volatile and synchronized?
Answer: volatile ensures visibility (changes are seen by all threads) but NOT atomicity. synchronized ensures both visibility AND atomicity (mutual exclusion). Use volatile for simple flags; use synchronized for compound operations.
Q5: What is an intrinsic lock (monitor)?
Answer: Every Java object has an intrinsic lock (monitor). When a thread enters a synchronized block/method, it acquires the object's lock. Other threads trying to acquire the same lock are blocked until it's released. Only one thread holds a given lock at a time.
Q6: What is the difference between synchronized(this) and synchronized(SomeClass.class)?
Answer: synchronized(this) locks on the current instance — different instances have different locks. synchronized(SomeClass.class) locks on the Class object — there's only one, so all instances share the same lock. Use class-level for static shared resources.
Q8: What is the difference between synchronized method and synchronized block?
A: Synchronized method locks the entire object (this) for instance methods or the Class object for static methods. Synchronized block can lock on any object and synchronize only the critical section. Block is preferred because: (1) finer granularity, (2) less lock contention, (3) can use different lock objects for different critical sections.
Q9: What is a deadlock and how do you prevent it?
A: Deadlock occurs when two or more threads are waiting for each other's locks forever. Prevention strategies: (1) Lock ordering — always acquire locks in the same order. (2) Lock timeout — use tryLock(timeout) with ReentrantLock. (3) Single lock — use one lock for all shared resources. (4) Avoid nested locks when possible.
Q10: What is the difference between object-level and class-level locking?
A: Object-level lock (synchronized on this): each instance has its own lock — different instances can be accessed concurrently. Class-level lock (synchronized on ClassName.class or static synchronized method): one lock for ALL instances — no two threads can execute any static synchronized method simultaneously.
Q11: What are the alternatives to synchronized in Java?
A: (1) ReentrantLock — explicit lock/unlock, supports fairness, tryLock, interruptible. (2) ReadWriteLock — allows concurrent reads. (3) StampedLock — optimistic reading. (4) Atomic* classes — lock-free operations. (5) volatile — visibility without atomicity. (6) ConcurrentHashMap — built-in thread safety.
Q12: Does synchronization guarantee visibility of changes across threads?
A: Yes! The Java Memory Model guarantees that when a thread releases a lock (exits synchronized), all its writes become visible to the thread that subsequently acquires the same lock. This "happens-before" relationship ensures both mutual exclusion AND memory visibility.
Continue the lesson path