Unlocking Java’s Condition Interface: How Locks Manage Wait/Notify
This article explains the Java Condition interface, its relationship with ReentrantLock and AQS, demonstrates usage examples, and delves into the internal waiting queue and signaling mechanisms that enable advanced thread coordination beyond basic synchronized monitors.
1. Introduction to Condition Interface
In the previous articles on AQS we learned that the internal class ConditionObject implements the Condition interface. Using ReentrantLock or ReentrantReadWriteLock, we can create one or more ConditionObject instances via newCondition().
When synchronized is used, any Java object can serve as a lock because every object inherits monitor methods ( wait, notify, notifyAll) from Object, enabling the classic wait/notify pattern.
The Condition interface also provides similar monitor methods that work with Lock to achieve wait/notify, but there are subtle differences in usage and capabilities.
Condition interface definition:
The methods defined in the interface are similar to those in Object.
2. Condition Interface Usage Example
Sample code (image):
Test code (image):
Building on the earlier article "Java Multithreading Programming – (5) Thread Communication Mechanisms", we replace the simple wait/notify implementation of a blocking queue with a Condition -based version. The following images illustrate the rewritten code:
3. Implementation Principles of Condition Interface
ConditionObject implements Condition as an inner class of AQS. Each Condition instance contains a FIFO waiting queue, which is the core of its wait‑notify mechanism.
Just like with synchronized, a thread must hold the associated lock before calling await() or signal().
1. Waiting Queue
When a thread invokes Condition.await(), it releases the lock, creates a node (using AQS’s Node class), and enqueues it at the tail of the condition’s waiting queue.
The queue maintains references to its head and tail. Adding a node simply links the previous tail’s nextWaiter to the new node and updates the tail reference. This update does not require CAS because the thread already holds the lock.
Unlike synchronized, which has a single monitor queue, a Lock can have multiple condition queues, each created via lock.newCondition() and sharing the same underlying synchronizer.
2. Waiting Implementation
Calling Condition.await() moves the current thread from the synchronizer’s queue to the condition’s waiting queue, releases the lock, and puts the thread into a waiting state.
3. Notification Implementation
Invoking Condition.signal() wakes the longest‑waiting node (the head of the queue) and transfers it back to the synchronizer’s queue.
The awakened thread exits the await() loop, re‑enters the synchronizer’s competition for the lock, and finally returns from await() with the lock held. Condition.signalAll() repeatedly applies signal() to every node in the waiting queue, moving all of them to the synchronizer’s queue and waking each thread.
References:
Partial content and screenshots from "Java Concurrency in Practice"
http://blog.csdn.net/ghsau/article/details/7481142
http://ifeve.com/understand-condition/
http://www.cnblogs.com/zhengbin/p/6420984.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack 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.
