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.

Programmer DD
Programmer DD
Programmer DD
Unveiling Java’s synchronized: How the JVM Implements and Optimizes 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).

synchronized bytecode (monitorenter/monitorexit)
synchronized bytecode (monitorenter/monitorexit)

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).

monitor internal structure
monitor internal structure

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.

lightweight lock flow
lightweight lock flow

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.

biased lock acquisition and release
biased lock acquisition and release

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.

heavyweight lock diagram
heavyweight lock diagram

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javalockingsynchronized
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.