52. The synchronized Keyword
synchronized gives a thread exclusive access to a block or method using the object's intrinsic monitor lock; it also establishes a happens-before relationship, guaranteeing visibility of writes.
Explanation
Every Java object has an intrinsic lock (monitor). synchronized(obj) { ... } acquires obj's lock before entering the block and releases it on exit (even if an exception is thrown). synchronized on an instance method uses this as the lock; on a static method it uses the Class object.
Two guarantees: mutual exclusion (only one thread in the block at a time) and visibility (when a thread releases the lock, all writes made inside the block are visible to any thread that subsequently acquires the same lock).
Pitfalls: synchronising on different objects gives no mutual exclusion (synchronized(a) and synchronized(b) don't block each other). Synchronising on this exposes the lock to callers who can also synchronise on this, potentially causing deadlocks. Prefer private final Object lock = new Object() for internal locking.
Example
public class SynchronizedDemo {
// Thread-safe counter using synchronized
static class Counter {
private int count = 0;
private final Object lock = new Object(); // private lock โ not exposed
public void increment() {
synchronized (lock) { count++; }
}
public int get() {
synchronized (lock) { return count; }
}
}
// Synchronized method โ uses 'this' as lock
static class SafeStack<T> {
private final java.util.ArrayDeque<T> deque = new java.util.ArrayDeque<>();
public synchronized void push(T item) { deque.push(item); }
public synchronized T pop() { return deque.pop(); }
public synchronized int size() { return deque.size(); }
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
int N = 1000;
Thread[] threads = new Thread[N];
for (int i = 0; i < N; i++) {
threads[i] = new Thread(counter::increment);
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("Counter: " + counter.get()); // 1000 (always correct)
// Without synchronization, count would be < 1000 due to lost updates
SafeStack<Integer> stack = new SafeStack<>();
stack.push(1); stack.push(2); stack.push(3);
System.out.println("Stack size: " + stack.size()); // 3
System.out.println("Popped: " + stack.pop()); // 3
}
}Sources
- https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html โ Synchronization tutorial
- https://docs.oracle.com/javase/specs/jls/se21/html/jls-17.html โ JLS: Memory model and happens-before