The function of Condition is to provide more precise control of the lock. The await() method in Condition is equivalent to the wait() method of Object, the signal() method in Condition is equivalent to the notify() method of Object, and the signalAll() in Condition is equivalent to the notifyAll() method of Object. The difference is that the wait(), notify(), and notifyAll() methods in Object are bundled with "synchronized lock" (synchronized keyword); and Condition needs to be bundled with "mutex"/"share lock".
Condition function list
// Causes the current thread to wait until it receives a signal or is interrupted. void await()// causes the current thread to remain in a waiting state before receiving a signal, being interrupted, or reaching the specified waiting time. boolean await(long time, TimeUnit unit)// causes the current thread to be in a waiting state until it receives a signal, is interrupted, or reaches the specified waiting time. long awaitNanos(long nanosTimeout)// causes the current thread to be in a waiting state before receiving the signal. void awaitUninterruptibly()// causes the current thread to remain in a waiting state until it receives a signal, is interrupted, or reaches the specified deadline. boolean awaitUntil(Date deadline)// Wake up a waiting thread. void signal()// Wake up all waiting threads. void signalAll()
Condition class usage example
Condition breaks down the Object monitor methods (wait, notify, and notifyAll) into completely different objects so that by combining these objects with any Lock implementation, each object provides multiple wait sets (wait-set). Among them, Lock replaces the use of synchronized methods and statements, and Condition replaces the use of Object monitor methods. The following is a previously written example of thread communication with implementation with Condition, the code is as follows:
public class ThreadTest2 { public static void main(String[] args) { final Business business = new Business(); new Thread(new Runnable() { @Override public void run() { threadExecute(business, "sub"); } }).start(); threadExecute(business, "main"); } public static void threadExecute(Business business, String threadType) { for(int i = 0; i < 100; i++) { try { if("main".equals(threadType)) { business.main(i); } else { business.sub(i); } } catch (InterruptedException e) { e.printStackTrace(); } } } } class Business { private boolean bool = true; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public /*synchronized*/ void main(int loop) throws InterruptedException { lock.lock(); try { while(bool) { condition.await();//this.wait(); } for(int i = 0; i < 100; i++) { System.out.println("main thread seq of " + i + ", loop of " + loop); } bool = true; condition.signal();//this.notify(); } finally { lock.unlock(); } } public /*synchronized*/ void sub(int loop) throws InterruptedException { lock.lock(); try { while(!bool) { condition.await();//this.wait(); } for(int i = 0; i < 10; i++) { System.out.println("sub thread seq of " + i + ", loop of " + loop); } bool = false; condition.signal();//this.notify(); } finally { lock.unlock(); } } } In Condition, replace wait() with await(), replace notify() with signal(), and replace notifyAll() with signalAll(). The traditional communication method of threads can be implemented. Note here that Condition is bound to a Lock. To create a Lock, you must use the newCondition() method.
In this way, Condition is no different from traditional thread communication. The power of Condition is that it can establish different Conditions between multiple threads. The following is a piece of code from the API to illustrate.
class BoundedBuffer { final Lock lock = new ReentrantLock();//Lock object final Condition notFull = lock.newCondition();//Write thread condition final Condition notEmpty = lock.newCondition();//Read thread condition final Object[] items = new Object[100];//Cached queue int putptr/*Write index*/, takeptr/*Read index*/, count/*Number of data exists in the queue*/; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length)//If the queue is full of notFull.await();//Block the write thread items[putptr] = x;//Assign if (++putptr == items.length) putptr = 0;//If the write index is written to the last position of the queue, set it to 0 ++count;//Number++ notEmpty.signal();//Wake up the read thread} finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0)//If the queue is empty notEmpty.await();//Block the read thread Object x = items[takeptr];//Pick the value if (++takeptr == items.length) takeptr = 0;//If the read index reads the last position of the queue, set it to 0 --count;//Number -- notFull.signal();//Wake up the write thread return x; } finally { lock.unlock(); } } } This is a cache area in a multi-threaded working environment. The cache area provides two methods, put and take, put is to store data, take is to retrieve data, and there is a cache queue inside. See the code for specific variables and methods descriptions. The functions implemented by this cache area class: multiple threads store data and retrieve data from it. The maximum cache value that the cache queue (first in, first out, then in, and then out) can cache is 100, and multiple threads are mutually exclusive. When the stored value in the cache queue reaches 100, the write thread will be blocked and the read thread will be awakened. When the stored value in the cache queue is 0, the read thread will be blocked and the write thread will be awakened. The following analysis of the code execution process:
1. A write thread executes and calls the put method;
2. To determine whether the count is 100, obviously there is no 100;
3. Continue to execute and deposit the value;
4. After determining the currently written index position ++, whether it is equal to 100. Equalize the write index value to 0 and count+1;
5. Wake up only one of the read thread blocking queues;
6. Execute a read thread and call the take method;
7. …
8. Wake up only one of the write thread blocking queues.
This is the power of multiple Conditions. Assuming that the cache queue is full, then the blocking is definitely the write thread, and the wake-up is definitely the read thread. On the contrary, the blocking is definitely the read thread, and the wake-up is definitely the write thread. So what will happen if there is only one Condition? The cache queue is full, this Lock doesn't know whether it is the read thread or the write thread. If the wake-up is the read thread, everyone is happy. If the wake-up is the write thread, then the thread has just been awakened and blocked again, and then wakes up again, which wastes a lot of time.