Fundamentals 11 min read

Understanding Java Heavyweight Locks: ObjectMonitor vs AQS

This article explains the differences and similarities between Java's ObjectMonitor and the AbstractQueuedSynchronizer (AQS) implementations of heavyweight locks, covering their internal data structures, waiting queues, lock acquisition order, and how synchronized interacts with wait/notify mechanisms.

Top Architect
Top Architect
Top Architect
Understanding Java Heavyweight Locks: ObjectMonitor vs AQS

Preface

Continuing from the previous ten‑question series on biased locks, this article upgrades to the heavyweight lock version to assess how far you are from mastering heavyweight locks. It is recommended to understand the implementation principles of heavyweight locks in Java before reading.

What are the similarities and differences between ObjectMonitor and AQS?

Why does ObjectMonitor need two waiting queues, cxq and entryList?

When do threads in the cxq queue move to EntryList?

What is the wake‑up order of multiple threads in the waiting queue?

Can threads wait/notify under biased lock and lightweight lock?

What are the differences between the cxq and waitset data structures?

After notify/notifyAll, which thread— the awakened wait thread or other waiting threads— grabs the lock first?

Does synchronized have a fairness logic similar to AQS's fair/unfair lock?

If you can answer these confidently, you may skip this article; otherwise, it should be helpful.

Terminology

Waiting queue : In mutex lock implementations, when a thread fails to acquire the lock it is placed into a queue waiting for the lock to be released. This article refers to it as the waiting queue.

Sync queue : When a thread calls wait , it is placed into another queue awaiting notify . This article calls it the sync queue.

Question Analysis

Question 1: Differences between ObjectMonitor and AQS Both are built on the monitor model, sharing a similar overall architecture with shared variables and waiting queues, but they differ in implementation details.

1) Shared variable : ObjectMonitor uses an owner field set via CAS to indicate the owning thread, while AQS uses an integer state . Consequently, ObjectMonitor needs a re‑entry counter, whereas AQS uses exclusiveOwnerThread to track the owner.

2) Waiting queue : ObjectMonitor employs two queues— cxq and entryList —whereas AQS uses a single queue.

3) Condition synchronization : AQS supports multiple condition variables per lock, offering more flexible wait/notify semantics. ObjectMonitor has only one waitset , shared by all threads.

4) Share mode : AQS’s share mode simplifies the implementation of read‑write locks.

Question 2: Why ObjectMonitor needs cxq and entryList Separating the queues reduces contention: entryList is accessed only during lock acquisition/release, avoiding CAS and spinning, while cxq handles other operations, saving resources.

Question 3: When does a thread move from cxq to EntryList A thread that fails to acquire the lock is first placed into cxq . When the owning thread releases the lock, it transfers waiting nodes from cxq into entryList .

Question 4: Wake‑up order in the waiting queue Upon lock release, the JVM first checks entryList . If it is non‑empty, the first node is awakened; otherwise, the first node in cxq is awakened.

Question 5: Can threads wait/notify under biased or lightweight lock Yes, because wait/notify requires a sync queue, which exists only in ObjectMonitor.

Question 6: Differences between cxq and waitset structures cxq is a doubly‑linked list using LIFO (last‑in‑first‑out) ordering. The diagram below shows that the most recently enqueued thread (t3) will be at the head and will acquire the lock first after release. waitset is a circular linked list using FIFO (first‑in‑first‑out) ordering.

Question 7: After notify/notifyAll, which thread gets the lock first 1) With notify , the head of the waitset is awakened. If entryList is non‑empty, the awakened thread is placed there; otherwise, it goes to cxq . Because cxq is LIFO, the awakened thread will usually acquire the lock before other waiting threads. 2) With notifyAll , all waitset nodes are moved to cxq . If entryList is non‑empty, its head gets the lock first; otherwise, the last node transferred from the waitset (which becomes the head of cxq ) acquires the lock.

Question 8: Does synchronized have fairness logic like AQS By default, threads entering a heavyweight lock first spin to acquire the lock, which corresponds to AQS’s non‑fair lock. Even if spinning fails, the LIFO behavior of cxq means later‑entered threads may acquire the lock before earlier ones, opposite to AQS’s typical FIFO fair lock.

HotSpot can adjust the fairness logic of heavyweight locks for different scenarios, but this requires modifying JVM compilation parameters rather than a simple startup flag.

Conclusion

The JVM’s synchronized heavyweight lock and JDK’s AQS are both designed based on the monitor model, sharing many similarities. Readers interested in deeper understanding should study the monitor model, which greatly aids comprehension of mutex locks.

Welcome to discuss and share your viewpoints; feel free to contact the author for questions.

Additional resources and community links are provided in the original article.

JavaJVMconcurrencyAQSheavyweight lockObjectMonitor
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.