When multiple threads concurrently read and write to the same shared mutable variables on the Heap, Race Conditions corrupt the data. Synchronization uses Monitor Locks (Intrinsic Locks) to guarantee that only one thread can execute a critical section at any given moment.
class Counter {
int count = 0;
// Synchronized keyword guarantees mutually exclusive access:
public synchronized void increment() {
count++; // 3 steps: read, add 1, write back!
}
}
public class SynchronizationDemo {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10_000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10_000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
// Guaranteed to be exactly 20,000 thanks to synchronization!
System.out.println("Final Counter: " + counter.count);
}
}
Prefer synchronized blocks over synchronized methods to reduce thread lock contention:
public void processPayment(double amount) {
// Non-critical code runs concurrently without locks:
logPaymentAttempt(amount);
// Critical section: Locks only during balance modification
synchronized (this) {
this.balance -= amount;
}
}
Improper locking can cause Deadlocks. Let us explore Deadlock prevention, wait(), and notify(). Proceed to: Java Deadlock & Wait / Notify →
Accelerate your Java mastery by studying these tightly connected core tutorials:
Avoiding locking traps.
Threads and concurrent execution.
Thread-safe concurrency.
Concurrency in modern Java 21.