F
Fatima Alam
Guest
Demystifying the synchronized Keyword in Java
If youโve ever written multi-threaded code in Java, youโve likely stumbled upon the synchronized keyword. At first glance, it looks like a magic wand to solve all concurrency problemsโbut as with most "magical" solutions, it comes with caveats.
In this post, weโll break down what synchronized really does, when it works, when it doesnโt, and weigh its pros and cons.
What Does synchronized Do?
The synchronized keyword is Javaโs built-in way to ensure mutual exclusion. In simple terms:
Only one thread at a time can execute a synchronized method or block thatโs locked on the same object.
It achieves this by acquiring a monitor lock (also called an intrinsic lock) on the object. Other threads trying to acquire the same lock are forced to wait until itโs released.
When Does synchronized Work?
synchronized is your friend when:
Multiple threads are accessing and modifying shared data.
You want to prevent race conditions.
You need to ensure memory visibility (changes made by one thread become visible to others).
You are protecting either:
Instance methods โ lock is taken on this.
Static methods โ lock is taken on the class object.
Code blocks โ lock is taken on any object you specify (synchronized(someObject) { ... }).
When Doesnโt It Help?
synchronized isnโt a silver bullet. It does not help if:
Threads synchronize on different objects (wrong lock = no safety).
Youโre dealing with deadlocks (threads waiting on each otherโs locks).
Performance is criticalโtoo much contention slows things down.
Youโre working with non-shared resources (local variables donโt need synchronization).
You need synchronization across JVMsโsynchronized only works within one JVM.
Example: Counter Without and With synchronized
Without Synchronization (Race Condition)
Why?
Let's look at the Timeline Graph
With Synchronization
How?
Timeline Graph
Continue reading...
If youโve ever written multi-threaded code in Java, youโve likely stumbled upon the synchronized keyword. At first glance, it looks like a magic wand to solve all concurrency problemsโbut as with most "magical" solutions, it comes with caveats.
In this post, weโll break down what synchronized really does, when it works, when it doesnโt, and weigh its pros and cons.

The synchronized keyword is Javaโs built-in way to ensure mutual exclusion. In simple terms:

It achieves this by acquiring a monitor lock (also called an intrinsic lock) on the object. Other threads trying to acquire the same lock are forced to wait until itโs released.

synchronized is your friend when:
Multiple threads are accessing and modifying shared data.
You want to prevent race conditions.
You need to ensure memory visibility (changes made by one thread become visible to others).
You are protecting either:
Instance methods โ lock is taken on this.
Static methods โ lock is taken on the class object.
Code blocks โ lock is taken on any object you specify (synchronized(someObject) { ... }).

synchronized isnโt a silver bullet. It does not help if:
Threads synchronize on different objects (wrong lock = no safety).
Youโre dealing with deadlocks (threads waiting on each otherโs locks).
Performance is criticalโtoo much contention slows things down.
Youโre working with non-shared resources (local variables donโt need synchronization).
You need synchronization across JVMsโsynchronized only works within one JVM.
Aspect | Pros ![]() | Cons ![]() |
---|---|---|
Thread Safety | Ensures only one thread accesses shared resource at a time. | Incorrect use (wrong lock) gives false sense of safety. |
Simplicity | Very easy to use (synchronized keyword is enough). | Can be misused easily, leading to bugs. |
Memory Visibility | Guarantees changes by one thread are visible to others. | Doesnโt prevent logical errors like deadlock or starvation. |
Flexibility | Can synchronize methods or specific code blocks. | Synchronizing too broadly (e.g., whole method) reduces performance. |
Reliability | Built-in, well-tested by JVM, no extra libraries needed. | Performance bottleneck if many threads contend for same lock. |
Granularity | Works well for small critical sections. | Coarse-grained locking makes code slower and less scalable. |


Code:
class Counter {
private int count = 0;
public void increment() {
count++; // not thread-safe
}
public int getCount() {
return count;
}
}
public class RaceConditionDemo {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final Count (expected 2000): " + counter.getCount());
}
}
Output -> ๐ Youโll often get a result less than 2000 because both threads modify count at the same time, causing lost updates.
Why?
Let's look at the Timeline Graph
Time โ | T1 | T2 |
---|---|---|
t1 | BEGIN | |
t2 | READ count = 0 | |
t3 | READ count = 0 | |
t4 | UPDATE count = 1 | |
t5 | UPDATE count = 1 (overwrites T1) | |
t6 | COMMIT (Final count = 1, lost update) | COMMIT |

Code:
class Counter {
private int count = 0;
public synchronized void increment() {
count++; // now thread-safe
}
public synchronized int getCount() {
return count;
}
}
public class SynchronizedDemo {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final Count (expected 2000): " + counter.getCount());
}
}
Output -> ๐ This time youโll reliably get 2000, because only one thread at a time can enter the increment() method.
How?
Timeline Graph
Time โ | T1 | T2 |
---|---|---|
t1 | ENTER synchronized block (lock acquired) | |
t2 | READ count = 0, UPDATE count = 1 | |
t3 | EXIT synchronized block (lock released) | |
t4 | ENTER synchronized block (lock acquired) | |
t5 | READ count = 1, UPDATE count = 2 | |
t6 | EXIT synchronized block (lock released) |
Continue reading...