How does synchronized work in Java, and what is the difference between a synchronized method and a synchronized block?
synchronized is built on intrinsic locks, also called monitors. Every Java object has one. When a thread enters a synchronized block or method, it acquires the monitor of the object named in the synchronized clause, and it releases it when the block exits — normally, by throwing, or by returning.
The difference between the two forms is which object is locked and how wide the lock is. A synchronized instance method locks this — the receiver of the call — for the whole method body. A synchronized static method locks the Class object of the declaring class. A synchronized block locks exactly the object you name, and only for the statements inside the braces.
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread a = new Thread(task);
Thread b = new Thread(task);
a.start();
b.start();
try {
a.join();
b.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("expected 2000, got " + counter.value());
}
}
class Counter {
private int count;
synchronized void increment() {
count++;
}
synchronized int value() {
return count;
}
}The output shows the count reaching exactly 2000, which is what the counter would report if the increment were atomic. Without the synchronization, the read-modify-write inside count++ could interleave between the two threads and lose an update.
The recommendation is to prefer a synchronized block on a private lock object over a synchronized method, for two reasons. A synchronized method exposes this as a lock, which means any other code that obtains a reference to your object can also lock it — including code you do not control. And a synchronized method holds the lock for its entire body, including the parts that do not need it, which is a lot of unnecessary contention in a method that does IO.