We learned about wait/ Notify in the previous sections and learned that their combination allows threads to avoid spinning, thus reducing performance and CPU waste. Today, I will introduce the enhanced version of Condition, which is the use of Condition. The overall knowledge is not very much, I believe that friends can easily master it.

Condition brief Introduction

Before Java1.5, developers mainly used await/notify for collaboration between threads. In 1.5, Condition was introduced as a replacement for the former. The usage scenarios of the above two are the same, both are used in some typical models, such as waiting-notification model and production-consumption model.

  • Usage scenarios

When thread 1 is in the process of execution and needs to [wait for some condition] to continue, thread 1 will actively call await() method and enter the blocking state. Thread 2 then completes the condition. When the condition is complete, thread 2 calls signal(), at which point the JVM looks for thread 1 from the blocking thread waiting on condition. Thread 1 receives the signal and its thread state becomes executable.

  • advantage

Wait /notify is a Low-level Java method that collaborates with the object monitor to complete threads, while Condition is a utility class in JUC that collaborates with Lock to complete process control. It is highly scalable and adds many features, such as support for non-response interrupts and support for multiple wait conditions.

Condition core method

Condition is an interface, so let’s look at the methods inside it:

  • Await:The current thread enters a wait until it receives a signal (signal) or interrupted;
  • Await (long time, TimeUnit unit) :The current thread enters a wait until it receives a signal or is interrupted and reaches the specified wait time (time);
  • AwaitNanos (Long nanosTimeout) : As above, the main difference is that the unit of time is different, and the current method returns a value representing the remaining timeout.
  • AwaitUninterruptibly () : The current thread waits until it receives a signal, and the method does not respond to interrupts;
  • AwaitUntil (Date deadline) : The current thread enters the wait state until it is notified, interrupted, or until a certain time. The method returns true if it is not notified by the specified time, false otherwise
  • Signal () : Wakes up a thread waiting on Condition that must acquire the lock associated with the Condition before returning from the wait method.
  • SignalAll () : Wakes up all threads waiting on the Condition. Threads that can return from the wait method must acquire the lock associated with the Condition.

Condition code demonstration

  • Basic usage

First let’s look at the basic usage of Condition. A thread calls the await method to start blocking and calls the signal method to wake up the waiting thread.

public class ConditionDemo { private ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); void method_1() throws InterruptedException { lock.lock(); Try {system.out.println (" condition not met, start blocking "); condition.await(); System.out.println(" condition satisfied, continue to execute "); }finally { lock.unlock(); } } void method_2() { lock.lock(); Try {system.out.println (" after executing the required condition, start waking up the waiting thread "); condition.signal(); }finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { ConditionDemo conditionDemo = new ConditionDemo(); New Thread(() -> {try {thread.sleep (1000); conditionDemo.method_2(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); Conditiondemo.method_1 (); }}Copy the code

The print result is as follows:

As you can see, thread 1 blocks to await() method until thread 2 calls signal() and thread 1 resumes execution.

One thing to note is that the waiting thread and the wake thread cannot be the same thread, for example, if the above example is changed to the following paragraph, the program will enter the endless wait. The reason for this is simple, too, because by the time the thread is done calling method_1, it is already blocked and will not continue executing method_2.

Conditiondemo.method_1 (); // Conditiondemo.method_1 (); conditionDemo.method_2();Copy the code
  • Condition implements the producer-consumer pattern

We implemented the producer-consumer pattern with wait/notify, and today we’ll look at the implementation using Condition.

public class ConditionDemo2 { private int queueMaxSize = 5; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueMaxSize); private Lock lock = new ReentrantLock(); private Condition consConditon = lock.newCondition(); private Condition prodConditon = lock.newCondition(); public static void main(String[] args) { ConditionDemo2 conditionDemo2 = new ConditionDemo2(); Producer producer = conditionDemo2.new Producer(); Consumer consumer = conditionDemo2.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread {@override public void run() {consume(); } private void consume() { while (true) { lock.lock(); Try {while (queue.size() == 0) {system.out.println (" queue empty, waiting for data "); try { prodConditon.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.poll(); consConditon.signalAll(); System.out.println(" queue.size() + queue.size() + "); } finally { lock.unlock(); }}}} // Class Producer extends Thread {@override public void run() {produce(); } private void produce() { while (true) { lock.lock(); Try {while (queue.size() == queueMaxSize) {system.out.println (" queue is full, waiting for consumers to consume "); try { consConditon.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.offer(1); prodConditon.signalAll(); System.out.println(" Insert elements, current number of queues "+ queue.size()); } finally { lock.unlock(); } } } } }Copy the code

The results are as follows:

Analyze the execution flow of the above code:

  • The producer thread acquires the lock and starts inserting data when the number of queues is less than the maximum number of queues.
  • After inserting the data, the producer callssignalAllWake up the blocking consumer thread;
  • The consumer thread attempts to acquire the lock. If it succeeds, it consumes the data in the queue. If it fails, the producer thread continues to insert data into the queue until the number of queues reaches its maximumawaitEnter into obstruction;
  • When the consumer finishes consuming, the consumer callssignalAllMethod wakes up the producer thread in the blocking. If the producer does not acquire the lock, the consumer will continue to consume until the queue is empty and the consumer will callawaitEnter the block.

Condition Matters needing attention

  • ConditionUsed to replacewait/notifyIn terms of usage and nature, they are basically the same.
  • awaitMethod child automatically releases held Lock Lock, this point andObject.wait()Again, no active release required.
  • callawaitMust hold the lock, otherwise an exception will be thrown, also andObject.wait()The same.

conclusion

Condition is easy to use. In combination with wait/notify, I believe you can also master it easily. I have been working on some other columns recently, so the update is also slow. The next chapter is expected to update the last process control class CyclicBarrier.

Pay attention to avoid getting lost

The above is the whole content of this issue, if there is any mistake, please leave a message for advice, thank you very much. I’m Gie, feel free to leave a comment if you have any questions, and we’ll see you next time at 🦮.

Original is not easy, your praise is my motivation to adhere to, if you think this article is a little useful to you, thank old tie for this article a praise, comment or forward, because this will be my output more quality articles of power, thank you!