`synchronized` is simpler and uses JVM monitors, giving mutual exclusion and a clear happens-before relationship. `ReentrantLock` is more flexible: `tryLock()`, timeouts, fairness options, and multiple `Condition`s—but you must always `unlock()` in `finally`.