Race conditions and synchronized
Watch an update get lost between two threads, then fix it with the simplest tool Java has.
Open this lesson in the learning hubKey points
count++is three steps: read, add, write. Two threads interleave, both read 7, both write 8, and one increment vanishes.- A race condition means the answer depends on timing. It passes on your laptop and fails under load.
synchronizedgives mutual exclusion: only one thread at a time holds that object's monitor.- It fixes visibility too. Everything done before releasing a lock is visible to the next thread that acquires it.
- Monitors are reentrant. A thread already holding the lock can enter another synchronized block on the same object.
- Lock on a
private final Object. Never lock on a String literal or a boxed number, since unrelated code shares those instances.
Example
public class Main {
static final Object LOCK = new Object();
static int unsafe = 0;
static int safe = 0;
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[4];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int n = 0; n < 100_000; n++) {
unsafe++; // read, add, write: 3 steps
synchronized (LOCK) { safe++; } // one indivisible step
}
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("expected : 400000");
System.out.println("unsafe : " + unsafe + " <- updates got lost");
System.out.println("safe : " + safe);
}
}
If two threads touch the same mutable data, one of them has to wait.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Multithreading course, and every lesson in it is listed on the Multithreading contents page.