Unveiling Java’s synchronized: How the JVM Implements and Optimizes Locks
This article explains the inner workings of Java's synchronized keyword, covering its implementation via monitorenter/monitorexit, the structure of object headers and monitors, and the series of lock optimizations introduced since JDK 1.6 such as spin locks, lock elimination, lock coarsening, lightweight, biased, and heavyweight locks.
1. Implementation Principle
synchronized guarantees that only one thread can execute a synchronized method or block at a time and also ensures memory visibility of shared variables. The lock is based on the object used as the monitor: the current instance for ordinary methods, the Class object for static methods, or any explicit object for synchronized blocks.
When a thread enters a synchronized block it must acquire the monitor; upon normal exit or exception the monitor is released.
2. Java Object Header
The HotSpot JVM stores a header in every object consisting of two parts: the Mark Word and a Klass Pointer. The Mark Word holds runtime data such as hash code, GC age, lock state, thread ID for biased locking, etc. It occupies two machine words on a 32‑bit VM (four on 64‑bit).
Mark Word changes its layout depending on the lock state (no‑lock, biased, lightweight, heavyweight).
3. Monitor
Each object is associated with a monitor, a native synchronization structure. The monitor contains fields such as Owner (current lock holder), EntryQ (semaphore for blocked threads), RcThis (number of threads waiting), Nest (re‑entrancy count), Candidate (used to avoid unnecessary wake‑ups).
4. Lock Optimizations in JDK 1.6
JDK 1.6 introduced several techniques to reduce the overhead of synchronization:
Spin Lock : a thread repeatedly checks a lock variable instead of blocking, useful when the lock is held only briefly.
Adaptive Spin : the spin count is adjusted dynamically based on recent lock acquisition history.
Lock Elimination : the JIT removes locks that are proven to have no data race via escape analysis.
Lock Coarsening : consecutive lock/unlock pairs are merged into a larger region to reduce lock operations.
Lightweight Lock : avoids OS mutexes by using CAS on the Mark Word; suitable when contention is low.
Biased Lock : eliminates CAS entirely for uncontended locks by biasing the lock toward the first thread that acquires it.
Heavyweight Lock : falls back to a traditional OS mutex when contention is high.
Spin Lock Details
A spin lock makes the thread loop for a short period, checking whether the lock becomes free. If the lock is not acquired after a configurable number of spins (default 10, controlled by -XX:PreBlockSpin), the thread blocks.
Lightweight Lock Acquisition
Check if the object is in the unlocked state; if so, allocate a lock record on the current thread’s stack.
Attempt a CAS to replace the Mark Word with a pointer to the lock record. Success means the thread now holds the lightweight lock.
If the CAS fails or the Mark Word points to another thread’s lock record, the lock inflates to a heavyweight lock.
Lightweight Lock Release
Retrieve the displaced Mark Word stored in the lock record.
CAS it back into the object’s Mark Word. If successful, the lock is released.
If the CAS fails, another thread is trying to acquire the lock, so the releasing thread must wake the waiting thread.
Biased Lock Mechanics
When a lock is biased, the Mark Word stores the owning thread ID and a bias flag. The lock is only revoked when another thread attempts to acquire it, causing a safepoint and possible inflation to a lightweight lock.
Heavyweight Lock
If contention escalates, the lock inflates to a heavyweight lock, which uses a native OS mutex (monitor) and incurs a costly transition between user and kernel mode.
5. Code Illustration
public class SynchronizedTest {
public synchronized void test1() {
// synchronized instance method
}
public void test2() {
synchronized (this) {
// synchronized block
}
}
}Running javap -c on the compiled class shows the monitorenter and monitorexit bytecode instructions for the synchronized block, while a synchronized method is marked with the ACC_SYNCHRONIZED flag in the method’s access flags.
Understanding these mechanisms helps developers write more efficient concurrent code, choose appropriate synchronization strategies, and diagnose performance issues related to lock contention.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
