Understanding Java synchronized and Lock Mechanisms: From Basic Locks to Advanced Optimizations
This article explains Java thread‑safety concepts, the synchronized keyword, its underlying monitor implementation, lock‑upgrade stages, JVM optimizations such as lock elimination and coarsening, and compares explicit Lock interfaces like ReentrantLock and ReadWriteLock, helping developers choose the appropriate synchronization tool.
Preface
Thread safety is a crucial concern in concurrent programming; it arises from shared data (critical resources) and multiple threads operating on that data simultaneously.
To ensure mutual exclusion, only one thread may hold the lock on shared data at a time while others wait, which is achieved by a mutex lock.
1. synchronized Lock Mechanism
1.1 Purpose of synchronized
synchronized grants the current thread ownership of an object lock, allowing exclusive execution of a method or code block and guaranteeing visibility.
The three main usage forms are:
Instance method synchronization – locks the current instance.
Static method synchronization – locks the class object.
Block synchronization – locks a specified object.
1.2 Underlying semantics
In the JVM, synchronized relies on a monitor associated with each object. When a thread acquires the monitor, it becomes locked; the monitor maintains two internal collections: _EntryList (blocked threads) and _WaitSet (waiting threads), each holding ObjectWaiter objects.
Thread entry and exit manipulate the monitor’s counter and owner fields, with wait() releasing the monitor and moving the thread to _WaitSet , and normal completion releasing the monitor via monitorexit .
1.3 Explicit vs. implicit synchronization
Explicit synchronization (synchronized blocks) uses the bytecode instructions monitorenter and monitorexit . Implicit synchronization (synchronized methods) is indicated by the ACC_SYNCHRONIZED flag in the method’s metadata.
1.3.1 Synchronized block implementation
The block is compiled to monitorenter at the start and monitorexit at the end. If the monitor’s entry count is zero, the thread acquires it; re‑entrance increments the count; otherwise the thread blocks until the monitor is released.
1.3.2 Synchronized method implementation
When a synchronized method is invoked, the JVM checks the ACC_SYNCHRONIZED flag, acquires the monitor before execution, and releases it after normal or exceptional completion.
1.4 JVM optimizations for synchronized
Early JVMs used heavyweight OS mutexes, but since JDK 6 the JVM performs several optimizations:
1.4.1 Lock upgrade
Locks progress through biased lock → lightweight lock → spin lock → heavyweight lock, using CAS and the object’s Mark Word to avoid OS mutexes when possible.
1.4.2 Lock elimination
JIT escape analysis removes locks that cannot be contended, reducing unnecessary synchronization overhead.
1.4.3 Lock coarsening
Adjacent synchronized blocks using the same lock are merged into a larger block to cut down on repeated lock acquisition.
1.5 Biased lock removal
From JDK 15 onward biased locks are disabled because they increase code complexity, provide diminishing performance gains, and interfere with modern thread‑pool designs.
2. Lock Interface Mechanism
Java 5 introduced the Lock interface in java.util.concurrent, providing explicit lock control via lock() and unlock() .
2.1 ReentrantLock
ReentrantLock implements Lock and offers features beyond synchronized, such as fairness policies, tryLock, interruptible lock acquisition, and multiple condition queues.
2.1.1 Differences from synchronized
ReentrantLock can be configured as a fair lock; synchronized cannot guarantee fairness.
ReentrantLock supports tryLock to test acquisition success; synchronized lacks this.
ReentrantLock allows interruptible lock acquisition; synchronized blocks cannot be interrupted while waiting.
ReentrantLock works with Condition objects for multiple wait‑sets; synchronized provides only a single monitor wait set.
Performance-wise, before JDK 6 ReentrantLock outperformed synchronized under high contention, but later JVM optimizations narrowed the gap.
2.2 ReadWriteLock
ReadWriteLock separates read and write access: multiple threads can hold a shared read lock concurrently, while write locks are exclusive, improving throughput for read‑heavy workloads.
2.3 ReentrantReadWriteLock
This concrete implementation of ReadWriteLock provides the described read‑write separation.
Overall, both synchronized and ReentrantLock are re‑entrant; the choice depends on the specific scenario, with synchronized being simpler and often sufficient after JVM optimizations.
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.