Fundamentals 15 min read

Understanding the Implementation of Java synchronized: Biased, Lightweight, and Heavyweight Locks

This article provides a comprehensive analysis of the HotSpot JVM's synchronized implementation, covering the object header, the three lock states (biased, lightweight, heavyweight), their acquisition and release processes, bytecode details, and the conditions that trigger lock upgrades and optimizations.

Java Captain
Java Captain
Java Captain
Understanding the Implementation of Java synchronized: Biased, Lightweight, and Heavyweight Locks

The article introduces a series of posts that deeply analyze the HotSpot JVM implementation of the synchronized keyword, aiming to clarify biased, lightweight, and heavyweight lock mechanisms and their lock‑upgrade processes.

It starts with a simple Java demo showing a synchronized block and a synchronized method:

public class SyncTest {
    public void syncBlock(){
        synchronized (this){
            System.out.println("hello block");
        }
    }
    public synchronized void syncMethod(){
        System.out.println("hello method");
    }
}

Using javap -v , the generated bytecode reveals the monitorenter and monitorexit instructions for the block and the ACC_SYNCHRONIZED flag for the method, indicating how the JVM handles lock acquisition and release.

The article then explains that every Java object has an object header consisting of a Mark Word and a Klass pointer; the Mark Word stores hash code, GC age, and lock state. Depending on the lock state, the Mark Word holds a biased thread ID, a pointer to a lock record, or a pointer to a monitor object.

Heavyweight (traditional) locks rely on OS mutexes (futex on Linux). The monitor object contains fields such as cxq , EntryList , WaitSet , and owner , managing waiting threads and lock ownership.

Lightweight locks avoid kernel transitions by creating a Lock Record on the thread stack, storing the original Mark Word (Displaced Mark Word) and a reference to the locked object. A CAS operation attempts to install the Lock Record’s address into the object's Mark Word; on failure, the lock inflates to heavyweight.

Biased locks, introduced in JDK 6, assume a single thread will repeatedly acquire the lock. When an object is first created, its Mark Word is in an anonymously biased state (thread ID = 0). The first thread to acquire the lock CASes its ID into the Mark Word. Subsequent acquisitions by the same thread require only simple pointer updates, while other threads trigger revocation or upgrade to lightweight locks. The article also describes bulk rebias and bulk revoke mechanisms that adjust the biasing strategy per class based on contention thresholds.

Finally, the article summarizes that Java's synchronized employs a three‑level lock hierarchy—biased → lightweight → heavyweight—upgrading when contention increases, and that lock downgrades are possible but rare.

javaJVMConcurrencylockingsynchronizedbiased lock
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

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