Previously, we had many questions about synchronization, but in reality, collaboration between threads was required.For example, the most classic producer-consumer model is that when the queue is full, producers need to wait for the queue to have space to continue to put their goods in it, while in the waiting period, producers must release their hold on the critical resources (that is, queues).Because if a producer does not release his or her possession of critical resources, consumers will not be able to consume the goods in the queue, leaving no room for the queue, and the producer will wait indefinitely.Therefore, in general, when the queue is full, the producer will surrender his or her possession of critical resources and go into a suspended state.Then wait for the consumer to consume the product, and the consumer notifies the producer that there is room in the queue.Similarly, when the queue is empty, the consumer must wait for the producer to notify it that there are items in the queue.This process of communicating with each other is collaboration between threads.
Today, let's explore the two most common ways of thread collaboration in Java: using Object.wait(), Object.notify(), and using Condition
The following is an outline of the text catalogue:
1.wait(), notify(), and notifyAll()
Condition
3. Realization of producer-consumer model
If something is wrong, please be more understanding and welcome criticism and correction.
Please respect the author's work and indicate the link in the original text for reprinting:
http://www.cnblogs.com/dolphin0520/p/3920385.html
I.wait(), notify(), and notifyAll()
wait(), notify(), and notifyAll() are methods in the Object class:
/** * Wakes up a single thread that is waiting on this object's * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's * monitor by calling one of the wait methods */ public final native void notify(); /** * Wakes up all threads that are waiting on this object's monitor. A * thread waits on an object's monitor by calling one of the * wait methods. */ public final native void notifyAll(); /** * Causes the current thread to wait until either another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object, or a * specified amount of time has elapsed. * <p> * The current thread must own this object's monitor. */ public final native void wait(long timeout) throws InterruptedException;
From the textual descriptions of these three methods, you can see the following information:
1) The wait(), notify(), and notifyAll() methods are local and final and cannot be overridden.
2) Calling the wait() method of an object can block the current thread, and the current thread must own the monitor (that is, lock) of the object
3) Calling an object's notify() method can wake up a thread waiting for the monitor of the object, and only one thread can wake up if multiple threads are waiting for the monitor of the object;
4) Calling the notifyAll() method wakes up all monitor s waiting for this object;
Friends may wonder why these three methods are not declared in the Thread class, but in the Object class (of course, since the Thread class inherits the Object class, Thread can also call three methods)?The problem is very simple, because each object has a monitor (that is, a lock), so let the current thread wait for a lock on an object, which of course should be used to manipulate it.Instead of operating with the current thread, because the current thread may wait for locks from multiple threads, it is very complex to operate with threads.
As mentioned above, if an object's wait() method is called, the current thread must have a monitor (that is, a lock) for that object, so the call to wait() method must be made in a synchronized block or synchronized method.
Calling the wait() method of an object is equivalent to having the current thread surrender the monitor of the object and enter a wait state for subsequent acquisition of the lock on the object (the sleep method in the Thread class suspends execution of the current thread for a period of time, giving other threads the opportunity to continue execution, but it does not release theElephant lock;
The notify() method wakes up a thread that is waiting for a monitor of the object. If multiple threads are waiting for the monitor of the object, only one of them can be waked up. It is not known which thread wakes up.
Similarly, when an object's notify() method is called, the current thread must also have a monitor for that object, so the call to the notify() method must be made in a synchronized block or synchronized method (synchronized block or synchronized method).
The nofityAll() method wakes up all monitor s waiting for the object, which is different from the notify() method.
One thing to note here is that the notify() and notifyAll() methods simply wake up the thread waiting for the monitor of the object, and do not determine which thread gets the monitor.
A simple example is given: If three threads Thread1, Thread2, and Thread3 are waiting for the monitor of the object objectA, Thread4 has the monitor of the object objectA, when the objectA.notify() method is called in Thread4, only one of Thread1, Thread2, and Thread3 can be waked up.Note that waking up does not mean getting objectA's monitor right away.If the objectA.notifyAll() method is called in Thread4, Thread1, Thread2, and Thread3 threads will all be awakened, depending on the scheduling of the operating system for which thread will be able to obtain the objectA monitor next.
In particular, it is important to note that waking up a thread does not mean that the object's monitor is acquired immediately. Only after notify() or notifyAll() is called and the synchronized block is exited, can the remaining threads acquire lock execution.
Looking at an example below shows that:
public class Test { public static Object object = new Object(); public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); } static class Thread1 extends Thread{ @Override public void run() { synchronized (object) { try { object.wait(); } catch (InterruptedException e) { } System.out.println("thread"+Thread.currentThread().getName()+"Lock acquired"); } } } static class Thread2 extends Thread{ @Override public void run() { synchronized (object) { object.notify(); System.out.println("thread"+Thread.currentThread().getName()+"Called object.notify()"); } System.out.println("thread"+Thread.currentThread().getName()+"Lock released"); } } }
No matter how many times you run it, the result must be:
Thread-1 called object.notify() Thread-1 released the lock Thread-0 acquired a lock
2. Condition
Conditions emerged in java 1.5 as a substitute for traditional Object wait(), notify() for interthread collaboration, which is safer and more efficient than using Object wait(), notify(), Condition 1 await(), signal().So Conditions are generally recommended, as described in the blog post Blocking Queues, which actually uses Conditions to simulate interthread collaboration.
- Condition is an interface, the basic methods are await() and signal() methods;
- Condition relies on the Lock interface, and the basic code for generating a Condition is lock.newCondition().
- Calling Condition's await() and signal() methods must be within lock protection, that is, between lock.lock() and lock.unlock before they can be used
await() in Conditon corresponds to wait() in Object;
signal() in Condition corresponds to notify() of Object;
signalAll() in Condition corresponds to notifyAll() of Object.
3. Realization of producer-consumer model
1. Use Object wait() and notify() to implement:
public class Test { private int queueSize = 10; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize); public static void main(String[] args) { Test test = new Test(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { consume(); } private void consume() { while(true){ synchronized (queue) { while(queue.size() == 0){ try { System.out.println("Queue empty, waiting for data"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.poll(); //Remove the first element of the queue each time queue.notify(); System.out.println("Remove an element from the queue, the queue remains"+queue.size()+"Elements"); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while(true){ synchronized (queue) { while(queue.size() == queueSize){ try { System.out.println("Queue full, waiting for free space"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.offer(1); //Insert one element at a time queue.notify(); System.out.println("Insert an element into the queue, leaving space in the queue:"+(queueSize-queue.size())); } } } } }
2. Implement with Condition
public class Test { private int queueSize = 10; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize); private Lock lock = new ReentrantLock(); private Condition notFull = lock.newCondition(); private Condition notEmpty = lock.newCondition(); public static void main(String[] args) { Test test = new Test(); Producer producer = test.new Producer(); Consumer consumer = test.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){ try { System.out.println("Queue empty, waiting for data"); notEmpty.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.poll(); //Remove the first element of the queue each time notFull.signal(); System.out.println("Remove an element from the queue, the queue remains"+queue.size()+"Elements"); } finally{ lock.unlock(); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while(true){ lock.lock(); try { while(queue.size() == queueSize){ try { System.out.println("Queue full, waiting for free space"); notFull.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.offer(1); //Insert one element at a time notEmpty.signal(); System.out.println("Insert an element into the queue, leaving space in the queue:"+(queueSize-queue.size())); } finally{ lock.unlock(); } } } } }