The Role of the synchronized
Keyword in the JVM
The synchronized
keyword in Java ensures thread safety by preventing multiple threads from accessing critical sections of code simultaneously. It is implemented at the JVM level using monitor locks (intrinsic locks).
1. What Does synchronized
Do?
When a thread enters a synchronized
method or block:
- It acquires a lock (monitor) on the specified object.
- No other thread can enter another
synchronized
block that locks the same object until the first thread exits. - When the thread exits, it releases the lock, allowing other threads to enter.
Example of synchronized
Preventing Race Conditions
public class Counter {
private int count = 0;
public synchronized void increment() { // Locks on 'this'
count++;
}
public synchronized int getCount() { // Locks on 'this'
return count;
}
}
If two threads try to execute increment()
at the same time, one must wait.This prevents race conditions.
2. How Does synchronized
Work in the JVM?
Internally, the JVM uses monitor locks (mutexes) to implement synchronized
.
Bytecode Representation
When you write:
public synchronized void increment() { count++; }
The compiled bytecode (via javap -c
) shows:
monitorenter
...
monitorexit
monitorenter
: Acquires the lock (monitor).monitorexit
: Releases the lock.
💡 Each object in Java has an implicit monitor lock. The thread holding the monitor owns the lock, blocking other threads.
3. Types of synchronized
in Java
1️⃣ synchronized
Instance Methods
Locks on the current object (this
), preventing multiple threads from accessing synchronized methods on the same instance.
public class BankAccount {
private int balance = 100;
public synchronized void deposit(int amount) { // Locks 'this'
balance += amount;
}
public synchronized int getBalance() { // Locks 'this'
return balance;
}
}
✅ Ensures atomic updates to balance
.
2️⃣ synchronized
Static Methods
Locks on the class object (ClassName.class
), ensuring only one thread can execute any static synchronized method of the class.
public class Logger {
public static synchronized void log(String message) {
System.out.println(message);
}
}
✅ Ensures thread safety across all instances of the class.
Equivalent to:
public static void log(String message) {
synchronized (Logger.class) { // Explicit locking
System.out.println(message);
}
}
3️⃣ synchronized
Blocks
Allows fine-grained control, locking only a specific part of the method instead of the whole method.
public class Inventory {
private int stock = 100;
private final Object LOCK = new Object(); // Custom lock
public void reduceStock() {
synchronized (LOCK) { // Only locks this section
if (stock > 0) {
stock--;
}
}
}
}
✅ Better performance because it locks only the critical section.
4. When to Use synchronized
?
✅ Use synchronized
when:
- Modifying shared data across threads.
- You need mutual exclusion (only one thread executes at a time).
- You need simplicity (easier to read than
ReentrantLock
).
❌ Avoid synchronized
when:
- You need non-blocking operations (
AtomicInteger
,ConcurrentHashMap
). - You need fine-grained locking (
ReentrantLock
is better).
5. Limitations of synchronized
Problem | Why? | Alternative |
---|---|---|
Performance Issues | Locks block other threads | Use lock-free algorithms (AtomicInteger ) |
Thread Contention | Too many threads waiting for a lock | Use ReentrantLock for fairness |
Deadlocks | If two threads lock resources in different orders | Always lock in a consistent order |
6. Summary
Type | Locks On | Best Use Case |
---|---|---|
Instance Method | this | Synchronizing per-object operations |
Static Method | ClassName.class | Synchronizing shared static resources |
Synchronized Block | Custom object | Fine-grained control, better performance |