1. Introduction
When multiple threads access and modify shared mutable state without coordination, the result is a race condition — the final outcome depends unpredictably on the timing/interleaving of thread execution, often producing incorrect results. Java provides the synchronized keyword to enforce mutual exclusion, ensuring that only one thread at a time can execute a critical section of code that touches shared state.
Cricket analogy: When two scorers at different ends of the stadium both try to update the same shared run tally at once without coordinating, the total can end up wrong—a race condition—so the scoring app enforces that only one scorer edits the tally at a time, like Java's synchronized keyword.
Synchronization in Java is built around intrinsic locks, also called monitor locks, which every Java object implicitly possesses. A thread must acquire an object's intrinsic lock before entering a synchronized block or method associated with that object, and it releases the lock automatically when it exits.
Cricket analogy: Every stadium's official scoreboard has a single physical control panel that only one operator can hold the key to at any moment—like every object's intrinsic monitor lock that a thread must acquire before entering a synchronized block.
2. Syntax
// Synchronized instance method — locks on 'this'
public synchronized void increment() {
count++;
}
// Synchronized static method — locks on the Class object
public static synchronized void staticIncrement() {
staticCount++;
}
// Synchronized block — lock on an explicit object
public void increment() {
synchronized (this) {
count++;
}
}3. Explanation
A race condition occurs, for example, when two threads both read a shared counter's value, each increment it locally, and then write it back — one of the increments can be lost because the read-modify-write sequence is not atomic. Marking the method or the relevant block as synchronized forces threads to execute that critical section one at a time, eliminating the race.
Cricket analogy: Two assistant scorers both read the current run total as 150, each independently add a boundary's 4 runs, and both write back 154—losing one boundary's runs because the read-update-write wasn't atomic; marking the tally update as synchronized forces one scorer to finish before the other starts, eliminating the lost run.
The lock used depends on how synchronized is applied. A synchronized instance method locks on 'this' — the specific object instance the method is called on. A synchronized static method locks on the Class object itself (e.g. MyClass.class), which is shared across all instances, so static synchronized methods across different objects still serialize with each other. A synchronized block lets you specify exactly which object's lock to acquire, which is useful for locking on a smaller, more specific object than 'this' to reduce contention.
Cricket analogy: A synchronized method updating a specific batsman's individual scorecard locks just that player's card (like 'this'), a synchronized static method updating the team's overall match record locks the entire team's shared ledger (like the Class object), and a synchronized block lets a scorer lock only the boundary-count section instead of the whole scorecard, reducing contention.
Deadlock risk: if Thread A holds lock 1 and waits for lock 2, while Thread B holds lock 2 and waits for lock 1, neither thread can proceed — this is a deadlock. Always acquire multiple locks in a consistent, agreed-upon order across all threads to avoid this. Also remember: synchronized instance methods lock on 'this', and static synchronized methods lock on the Class object — these are two different locks, so a static synchronized method does NOT block a non-static synchronized method on the same object.
4. Example
public class SynchronizationDemo {
private int count = 0;
public synchronized void increment() {
count++;
}
public static void main(String[] args) throws InterruptedException {
SynchronizationDemo demo = new SynchronizationDemo();
int numThreads = 4;
int incrementsPerThread = 10000;
Thread[] threads = new Thread[numThreads];
for (int i = 0; i < numThreads; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < incrementsPerThread; j++) {
demo.increment();
}
});
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("Final count: " + demo.count);
System.out.println("Expected: " + (numThreads * incrementsPerThread));
}
}5. Output
Final count: 40000
Expected: 40000
(With synchronized removed from increment(), the final count would
likely be LESS than 40000 due to lost updates from the race condition.)6. Key Takeaways
- A race condition happens when multiple threads access shared mutable state without coordination, producing unpredictable results.
- The synchronized keyword enforces mutual exclusion using an object's intrinsic (monitor) lock.
- Synchronized instance methods lock on 'this'; synchronized static methods lock on the Class object.
- synchronized blocks allow locking on a specific, explicitly chosen object for finer-grained control.
- Deadlock occurs when two or more threads wait on each other's locks in a circular fashion — avoid by acquiring locks in a consistent order.
- Synchronization adds overhead, so it should be applied only to the minimal necessary critical section.
Practice what you learned
1. What does a synchronized instance method lock on?
2. What does a synchronized static method lock on?
3. What is a race condition?
4. What is a deadlock?
5. Can a static synchronized method and a non-static synchronized method on the same object run concurrently on two different threads?
Was this page helpful?
You May Also Like
Multithreading in Java
Learn how Java creates and runs multiple threads concurrently using the Thread class and Runnable interface, and why start() and run() behave so differently.
Thread Lifecycle in Java
Understand the states a Java thread passes through — from NEW to TERMINATED — as defined by the Thread.State enum, and what causes each transition.
static Keyword in Java
Learn how the static keyword creates class-level fields, methods, and blocks shared across all objects in Java.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics