Synchronization
Use synchronized, volatile, and atomic classes to safely share data between threads.
Overview
Key Points
- •synchronized(obj) { ... } acquires the monitor of obj before entering
- •synchronized methods use this (or the class) as the monitor
- •Only one thread can hold a monitor at a time; others wait
- •volatile guarantees visibility but not atomicity. Suitable for single-variable flags, not compound operations
- •AtomicInteger: compareAndSet, incrementAndGet, addAndGet are atomic
- •Race condition: two threads read, modify, and write the same variable non-atomically
- •Deadlock: two threads each wait for a lock the other holds. Avoid by always acquiring locks in the same order
Examples
Race condition and synchronized fix
public class Main {
static int counter = 0;
static synchronized void increment() {
counter++; // now atomic: only one thread at a time
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
increment();
}
});
}
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
System.out.println("Final counter: " + counter);
// Without synchronized: likely less than 100000
// With synchronized: always 100000
}
}Without synchronized, counter++ is three non-atomic steps (read, add, write). Two threads can interleave and one increment is lost. synchronized ensures the whole method is atomic.
AtomicInteger for lock-free counting
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) throws InterruptedException {
AtomicInteger counter = new AtomicInteger(0);
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.incrementAndGet(); // atomic, no lock needed
}
});
}
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
System.out.println("Final counter: " + counter.get()); // always 100000
System.out.println("Compare-and-set: " + counter.compareAndSet(100000, 0));
System.out.println("After reset: " + counter.get());
}
}AtomicInteger is faster than synchronized for single-value counters because it uses CPU-level compare-and-swap (CAS) instructions instead of a kernel-level lock.
volatile for visibility and synchronized block
public class Main {
static volatile boolean running = true; // volatile: changes visible across threads
static class Counter {
private int count = 0;
synchronized void increment() { count++; }
synchronized int get() { return count; }
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread worker = new Thread(() -> {
while (running) { // reads running from main memory due to volatile
counter.increment();
}
System.out.println("Worker stopped. Count: " + counter.get());
});
worker.start();
Thread.sleep(10); // let worker run a bit
running = false; // this write is immediately visible to worker due to volatile
worker.join();
}
}volatile ensures the worker sees the updated value of running immediately. Without volatile, the JVM can cache running in the worker thread and never see the update from main.
Common Mistakes
- Using synchronized on different objects for the same data. If method A synchronizes on this and method B synchronizes on a different object, they do not protect the same critical section
- Over-synchronizing: holding a lock longer than necessary hurts performance. Use the smallest possible synchronized block
- Using volatile for compound operations like counter++. volatile does not make the read-modify-write sequence atomic. Use AtomicInteger or synchronized for compound operations
Note
For simple counters and flags, prefer AtomicInteger/AtomicBoolean over synchronized. For protecting larger state or multiple variables, use synchronized. For one-time flags, consider volatile.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4