`synchronized` jest prostsze i używa monitorów JVM, dając wzajemne wykluczenie oraz jasne happens-before. `ReentrantLock` jest bardziej elastyczny: `tryLock()`, timeouty, opcje fairness i wiele `Condition` — ale musisz zawsze robić `unlock()` w `finally`.