This blog will introduce the Condition that is often used with locks in JUC. The content includes:
Introduction to Condition
Condition function list
Condition example
Introduction to Condition
Condition‘s role is to control the lock more precisely. 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(), notifyAll() methods in Object are bundled with synchronized lock. Condition needs to be bundled with mutually exclusive lock/shared lock in use.
// Causes the current thread to wait until it is signalled or interrupted voidawait() // Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses. booleanawait(long time, TimeUnit unit) // Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses. longawaitNanos(long nanosTimeout) // Causes the current thread to wait until it is signalled. voidawaitUninterruptibly() // Causes the current thread to wait until it is signalled or interrupted, or the specified deadline elapses. booleanawaitUntil(Date deadline) // Wakes up one waiting thread. voidsignal() // Wakes up all waiting threads. voidsignalAll()
Condition example
Example 1: demonstrate sleep/wake of a thread through Object’s wait(), notify()
try { System.out.println(Thread.currentThread().getName()+" wakup others"); // wake up other thread wait on condition condition.signal(); } finally { // release lock lock.unlock(); } } } }
Result:
1 2 3 4
main start ta main block ta wake up others main continue
From Example 1 and Example 2, we know that the methods of Condition and Object have the following correspondence:
1 2 3 4
Object Condition Sleep wait await Wake up one thread notify signal Wake up all thread notifyAll signalAll
In addition to supporting the above functions, Condition is more powerful: it can control the sleep and wake up of multi-thread more finely. For the same lock, we can create multiple Condition and use different Condition in different situations.
For example, if multiple threads read/write the same buffer: when writing data to the buffer, wake up the read thread. When reading data from the buffer, wake up the writing thread. And when the buffer is full the write thread needs to wait. When the buffer is empty, the read thread needs to wait. If you use wait(), notify(), notifyAll() in the Object to implement the buffer, when you need to wake up the read thread after writing data to the buffer, it is not possible to explicitly use notify() or notifyAll() to wake up the read thread, and only wake up all threads through notifyAll() (but notifyAll() can not distinguish whether the wake up thread is a read thread or a write thread). However, through Condition, you can clearly wake up the reader thread.
publicclassBoundedBuffer{ final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[5]; int putptr, takeptr, count;
public Object take()throws InterruptedException { try { // if buffer is empty, wait // until buffer has items, take it while (count == 0) { notEmpty.await(); }
// take x from buffer Object x = items[takeptr];
if (++takeptr == items.length) { takeptr = 0; }
--count;
// wake up put thread, as there are space in the buffer notFull.signal();
System.out.println(Thread.currentThread().getName() + " take " + x);
return x; } finally { lock.unlock(); } } }
publicclassConditionWait2{ privatestatic BoundedBuffer bb = new BoundedBuffer();
publicstaticvoidmain(String[] args){ for (int i = 0; i < 10; i++) { new PutThread("p" + i, i).start(); new TakeThread("t" + i).start(); } }
p1 put 1 p4 put 4 p5 put 5 p0 put 0 p2 put 2 t0 take 1 p3 put 3 t1 take 4 p6 put 6 t2 take 5 p7 put 7 t3 take 0 p8 put 8 t4 take 2 p9 put 9 t5 take 3 t6 take 6 t7 take 7 t8 take 8 t9 take 9
BoundedBuffer is a buffer with a capacity of 5, the buffer stores Object objects, and supports multi-threaded read/write buffer. When multiple threads operate on a BoundedBuffer object, they perform mutually exclusive access to the buffer items through the mutex lock. All threads under the same BoundedBuffer object share the two conditions notFull and notEmpty.
notFull is used to control the write buffer, and notEmpty is used to control the read buffer. When the buffer is full, the thread that called put() will execute notFull.await() to wait. When the buffer is not full, it will add the object to the buffer and count+1 the capacity of the buffer. Finally, call notEmpty.signal() buffers the waiting thread on notEmpty() (the thread that calls notEmpty.await()). In short, notFull controls the buffer writing, and when the data is written to the buffer, the waiting thread on notEmpty will be woken up.
In the same way, notEmpty controls the buffer reading, and will wake up the waiting thread on notFull after reading the buffer data.
In the main function of ConditionTest2, start 10 write threads to continuously write data to the BoundedBuffer (write 0-9). At the same time, also start 10 read threads to continuously read from the BoundedBuffer data.
Brief analysis of the operation results:
1 2 3 4 5 6 7 8 9 10 11 12 13
1, p1 thread writes 1 to the buffer. The buffer data | 1 | | | | | 2, p4 thread writes 4 to the buffer. The buffer data | 1 | 4 | | | | 3, p5 thread writes 5 to the buffer. The buffer data | 1 | 4 | 5 | | | 4, p0 thread writes 0 to the buffer. The buffer data | 1 | 4 | 5 | 0 | | 5, p2 thread writes 2 to the buffer. The buffer data | 1 | 4 | 5 | 0 | 2 |
At this point, the buffer capacity is 5: the buffer is full! If at this time, there is a write thread who wants to write data to the buffer, it will call notFull.await() in put to wait, until buffer is not full
6, t0 thread takes 1 from the buffer. The buffer data | | 4 | 5 | 0 | 2 | 7, p3 thread writes 3 to the buffer. The buffer data | 3 | 4 | 5 | 0 | 2 | 8, t1 thread takes 4 from the buffer. The buffer data | 3 | | 5 | 0 | 2 | 9, p6 thread writes 6 to the buffer. The buffer data | 3 | 6 | 5 | 0 | 2 | ...