Fundamentals 17 min read

What Really Happens Inside Java’s synchronized? A Deep Dive into JVM Lock Mechanisms

This article examines the inner workings of Java’s synchronized keyword by analyzing HotSpot 1.8 source code, revealing how lightweight, heavyweight, biased, and adaptive spin locks operate, how lock inflation occurs, and how the JVM manages monitor objects, CAS operations, and thread queues to implement synchronization.

ITPUB
ITPUB
ITPUB
What Really Happens Inside Java’s synchronized? A Deep Dive into JVM Lock Mechanisms

The author investigated the synchronized keyword by reading the HotSpot 1.8 source code, discovering that when a lightweight lock’s CAS fails the JVM does not spin; it inflates directly to a heavyweight lock. Spin only appears after the lock has been upgraded.

From Heavyweight Lock

Before Java 1.6 synchronized was implemented solely as a heavyweight lock, using the operating system’s mutex (e.g., pthread_mutex) which incurs a costly user‑kernel context switch.

Let’s look at the implementation of a heavyweight lock.

When a method or block is marked with synchronized, the compiler emits the bytecode instructions monitorenter and monitorexit. These instructions operate on a monitor object that is associated with the target object (the lock object). Each Java object has an internal monitor reference; acquiring the lock means obtaining ownership of that monitor.

The generated bytecode for a simple method shows a monitorenter before the critical section and a monitorexit after it, with the latter also handling exceptional exits to avoid deadlocks.

When a method is synchronized, the method’s access flags include ACC_SYNCHRONIZED (0x0021), which the JVM recognises at runtime to perform the same lock‑acquisition logic.

Monitor Internals

The monitor implementation in HotSpot is the C++ class ObjectMonitor. It contains fields such as _owner, _recursions, and queues for waiting threads ( _cxq and _EntryList).

Acquiring a heavyweight lock executes ObjectMonitor::enter. The thread attempts a CAS on _owner; success means the lock is obtained, and the recursion count is incremented for re‑entrancy.

If the CAS fails, the thread enters a loop that may perform adaptive spinning ( TrySpin_VaryDuration) before eventually creating an ObjectWaiter and enqueuing itself in _cxq. When the queue is full or spinning fails, the thread blocks via pthread_mutex_lock.

Releasing the lock calls ObjectMonitor::exit. The recursion count is decremented; when it reaches zero, the monitor clears _owner and wakes up waiting threads according to the queue mode (QMode 2, 3, 4, etc.).

Wait and Notify

Calling Object.wait() moves the current thread into the _waitSet (a double‑linked list) and then releases the monitor via ObjectMonitor::exit. notify and notifyAll remove nodes from _waitSet and place them back into the appropriate waiting queue, waking them for the next lock acquisition attempt.

Adaptive Spinning

To avoid the overhead of immediate blocking, the JVM employs adaptive spinning: a thread that fails to acquire a heavyweight lock will spin for a short, dynamically‑adjusted duration (implemented in TrySpin and TrySpin_VaryDuration). If the lock becomes free during this spin, the thread can acquire it without a context switch.

Lightweight Lock

When contention is low, the JVM prefers a lightweight lock. The lock record ( LockRecord) is allocated on the current stack frame, and the object's MarkWord is copied there (the “displaced header”). A CAS then points the object's header to this LockRecord. If the CAS succeeds, the thread holds the lightweight lock; re‑entrancy is indicated by a null dhw field.

Unlocking a lightweight lock restores the original MarkWord from the stack via CAS. If the CAS fails because another thread has contested the lock, the lock inflates to a heavyweight lock.

Biased Lock

For the common case where a single thread repeatedly acquires the same lock, the JVM uses biased locking. The thread’s ID is stored in the MarkWord with the bias flag (bits 101). Subsequent lock acquisitions simply check the flag and the stored thread ID, avoiding any CAS.

If contention appears, the biased lock is revoked and the lock upgrades to a lightweight (or heavyweight) lock. The JVM tracks revocation counts per class (e.g., BiasedLockingBulkRebiasThreshold and BiasedLockingBulkRevokeThreshold) and may disable biased locking for a class after many revocations.

Summary

The synchronized mechanism in HotSpot combines several lock states—biased, lightweight, heavyweight—and uses CAS, adaptive spinning, and OS mutexes to balance performance and correctness. Understanding the monitor object, the MarkWord layout, and the lock‑inflation path provides deep insight into Java concurrency and helps answer interview questions about JVM internals.

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.

JavaJVMconcurrencysynchronizedMonitor
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

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.