Understanding Java's synchronized Implementation and Its Differences from Lock
This article explains how Java's synchronized keyword works at the bytecode level using monitorenter/monitorexit, compares synchronized methods and blocks with explicit Lock implementations, and discusses performance, interrupt handling, and usage differences for multithreaded backend development.
In Java every object can serve as a lock, and the synchronized keyword provides the basic mechanism for synchronization.
For a normal synchronized method, the lock is the current instance object; for a static synchronized method, the lock is the Class object; for a synchronized block, the lock is the object specified in the parentheses.
When a thread enters a synchronized block it must acquire the monitor associated with the target object, and it releases the monitor when exiting or when an exception is thrown. The following simple code illustrates a synchronized method and a synchronized block:
package cn.alibab.javap;
public class SynchronizedTest {
public synchronized void test1() {
}
public void test2() {
synchronized (this) {
}
}
}Using the javap tool to inspect the compiled class file shows that synchronized blocks are implemented with the monitorenter and monitorexit bytecode instructions, while synchronized methods are marked with the ACC_SYNCHRONIZED flag in the method's access flags.
The article then compares synchronized with the Lock interface (e.g., ReentrantLock). Key differences include: synchronized is a language keyword; Lock is an interface.
When an exception occurs, synchronized automatically releases the lock, whereas a Lock must be released manually with unlock(), which can lead to deadlocks if omitted. Lock supports interruptible lock acquisition via interrupt, while synchronized cannot be interrupted. Lock provides tryLock to test lock acquisition and offers condition variables for advanced thread coordination, which synchronized lacks.
In high contention scenarios, Lock generally outperforms synchronized because it uses optimistic locking (CAS) and can implement read‑write locks.
Code example for using a Lock with a Condition:
//Condition defines await/signal methods
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
...
condition.await();
...
condition.signal();
condition.signalAll();Regarding performance, early Java versions (1.5) made synchronized relatively slow, but from Java 1.6 onward the JVM introduced optimizations such as biased locking, lightweight locking, lock elision, and lock coarsening, narrowing the performance gap with Lock. Nevertheless, in extremely high contention environments, Lock still tends to be faster.
In summary, synchronized offers simplicity and automatic lock release, making it suitable for low‑contention cases, while Lock provides greater flexibility, explicit control, and better performance under heavy contention, at the cost of more complex code and manual unlock handling.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
