Deep Dive into Java synchronized: Implementation, Lock Types, and JVM Internals
This article provides a comprehensive analysis of the HotSpot JVM implementation of the synchronized keyword, explaining object headers, biased, lightweight, and heavyweight locks, their acquisition, release, and upgrade processes, and includes detailed source code and bytecode examples.
Many articles discuss the underlying implementation of synchronized , but most are either inaccurate or superficial. This series offers a thorough analysis of HotSpot's synchronized lock implementation, covering biased lock, lightweight lock, heavyweight lock, their acquisition, release, and upgrade mechanisms, with source code inspection.
1. Introduction to synchronized
Java provides two basic synchronization constructs: synchronized methods and synchronized blocks. The following demo illustrates both forms:
public class SyncTest {
public void syncBlock(){
synchronized (this){
System.out.println("hello block");
}
}
public synchronized void syncMethod(){
System.out.println("hello method");
}
}When SyncTest.java is compiled, the bytecode for synchronized blocks and methods differs slightly. Using javap -v we can see the relevant instructions:
{
public void syncBlock();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter // enters synchronized block
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String hello block
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit // exits synchronized block
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
}
public synchronized void syncMethod();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED // ACC_SYNCHRONIZED flag added
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String hello method
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: returnThe monitorenter and monitorexit instructions correspond to entering and exiting a synchronized block. Two monitorexit instructions ensure the lock is released even when an exception occurs, implementing an implicit try‑finally. For synchronized methods, the JVM checks the ACC_SYNCHRONIZED flag and attempts to acquire the lock before method execution.
2. Types of Locks
Traditional (heavyweight) locks rely on OS synchronization primitives such as mutexes and futexes, involving user‑kernel transitions and higher overhead. When contention is low, lightweight and biased locks provide better performance.
Object Header
Every Java object has a header consisting of a mark word and a class pointer (type pointer). The mark word stores hash code, GC information, and lock state. Its layout varies with the lock state:
In biased lock state, the mark word holds the biased thread ID; in lightweight lock state, it holds a pointer to a lock record in the thread stack; in heavyweight lock state, it points to a monitor object on the heap.
2. Heavyweight Lock
A heavyweight lock uses a monitor object that contains a contention queue (cxq), entry list, wait set, and owner thread. When a thread attempts to acquire a lock that is already held, it creates an ObjectWaiter and enqueues it in cxq , then blocks. The owning thread releases the lock and moves waiting threads from cxq to EntryList , waking them up. Calls to Object.wait() move the waiter to WaitSet and release the lock; notify moves it back to EntryList .
3. Lightweight Lock
When there is little contention, the JVM creates a lock record on the thread stack, containing a copy of the object's original mark word (displaced mark word) and a reference to the object. The lock acquisition process is:
Create a lock record and set its obj field to the lock object.
Attempt a CAS to store the lock record address into the object's mark word. If the object is unlocked, the CAS succeeds and the thread obtains the lightweight lock.
If the current thread already holds the lock, the displaced mark word is set to null to indicate re‑entrance.
If CAS fails due to contention, the lock inflates to a heavyweight lock.
Unlocking involves scanning the thread stacks for lock records that reference the object, restoring the original mark word via CAS, or inflating to a heavyweight lock if restoration fails.
4. Biased Lock
Biased locking optimizes the case where a lock is always acquired by the same thread. When an object is created with biasing enabled, its mark word is in an anonymously biased state (thread ID = 0). The first acquisition by a thread performs a CAS to set the thread ID. Subsequent acquisitions by the same thread require only simple pointer updates without CAS.
If another thread attempts to acquire the lock, the JVM may revoke the bias at a safepoint, converting the object to an unlocked state or inflating it to a lightweight lock. Bulk rebias and bulk revoke mechanisms operate at the class level to avoid excessive bias revocation when many objects of the same class are involved.
3. Summary
Java's synchronized uses three lock states—biased, lightweight, and heavyweight—corresponding to single‑thread ownership, low‑contention alternating ownership, and high‑contention scenarios. Locks upgrade from biased to lightweight to heavyweight as contention increases, and can downgrade under strict conditions. This article introduced the basic concepts; subsequent articles will provide deeper source‑code analysis.
Source: https://blog.csdn.net/luoweifu/article/details/46613015
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.
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.