synchronized in detail

Multithreading · lesson 17 of 38 · 4 min read

Know exactly which object a synchronized block locks, and which objects you must never lock on.

Open this lesson in the learning hub

Key points

  • Every object owns one monitor. synchronized means: hold that monitor, or wait outside until the owner leaves.
  • A synchronized instance method locks this. A static one locks the Class object. Those are two different locks.
  • A synchronized block lets you choose the monitor and keep the critical section short. Prefer it to locking a whole method.
  • Monitors are reentrant. A thread already holding one can enter another block on the same object without blocking itself.
  • Lock a private final Object. Locking a String literal, a boxed Integer or this lets unrelated code lock you out.
  • Releasing a monitor publishes every write made inside it to the next thread that acquires it. Locking buys visibility too.

Example

public class Main {
    static int shared = 0;
    static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        Counter c = new Counter();
        Thread[] ts = new Thread[4];
        for (int i = 0; i < ts.length; i++) {
            ts[i] = new Thread(() -> {
                for (int n = 0; n < 50_000; n++) {
                    c.bump();                          // monitor = the Counter instance
                    synchronized (LOCK) { shared++; }  // monitor = one private object
                }
            });
            ts[i].start();
        }
        for (Thread t : ts) t.join();

        System.out.println("instance monitor : " + c.value());
        System.out.println("block monitor    : " + shared);
        System.out.println("reentrant call   : " + c.bumpTwice());
        System.out.println("class monitor    : " + describe());
    }

    static synchronized String describe() {            // static locks Main.class instead
        return "static synchronized locks Main.class";
    }

    static class Counter {
        private int value;
        synchronized void bump() { value++; }
        synchronized int value() { return value; }
        synchronized int bumpTwice() { bump(); bump(); return value; }  // reentrant
    }
}

Know which object you are locking, and keep the block small.

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.