From what I understand, the synchronized keyword is actually a moderately heavyweight recursive (re-entrant) lock.
For instance, the following (horrible) code would not deadlock:
public static Object lock = new Object();int recurCount = 0;public int fLocktorial(int n) { synchronized(lock) { recurCount++; if (n <= 0) return 1; return n * fLocktorial(n-1); }}
Implementing this requires the maintenance of additional state and logic within the lock, which may contribute to its lower performance over atomics and other primitives. However, it does allow you to grab locks arbitrarily inside functions, without worrying if a caller has already obtained the lock. Locks implemented naively using Atomics would deadlock in this case.
Additionally, synchronized may yield performance benefits if large amounts of processing are done within the lock. Getting a lock only has a performance hit once, while atomics force a core synchronization per operation. This flushes the processor pipeline, impacting performance.