Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例

如何使用Wait

尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单。如果你在面试中让应聘者来手写代码,用wait和notify解决生产者消费者问题,我几乎可以肯定他们中的大多数都会无所适从或者犯下一些错误,例如在错误的地方使用 synchronized 关键词,没有对正确的对象使用wait,或者没有遵循规范的代码方法。说实话,这个问题对于不常使用它们的程序员来说确实令人感觉比较头疼。

第一个问题就是,我们怎么在代码里使用wait()呢?因为wait()并不是Thread类下的函数,我们并不能使用Thread.call()。事实上很多Java程序员都喜欢这么写,因为它们习惯了使用Thread.sleep(),所以他们会试图使用wait() 来达成相同的目的,但很快他们就会发现这并不能顺利解决问题。正确的方法是对在多线程间共享的那个Object来使用wait。在生产者消费者问题中,这个共享的Object就是那个缓冲区队列。

第二个问题是,既然我们应该在synchronized的函数或是对象里调用wait,那哪个对象应该被synchronized呢?答案是,那个你希望上锁的对象就应该被synchronized,即那个在多个线程间被共享的对象。在生产者消费者问题中,应该被synchronized的就是那个缓冲区队列。

永远在循环(loop)里调用 wait 和 notify,不是在 If 语句

现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!

就像我之前说的一样,在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。

Java wait(), notify(), notifyAll() 范例

下面我们提供一个使用wait和notify的范例程序。在这个程序里,我们使用了上文所述的一些代码规范。我们有两个线程,分别名为PRODUCER(生产者)和CONSUMER(消费者),他们分别继承了了Producer和Consumer类,而Producer和Consumer都继承了Thread类。Producer和Consumer想要实现的代码逻辑都在run()函数内。Main线程开始了生产者和消费者线程,并声明了一个LinkedList作为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往LinkedList里插入随机整数直到LinkedList满。我们在while(queue.size == maxSize)循环语句中检查这个条件。请注意到我们在做这个检查条件之前已经在队列对象上使用了synchronized关键词,因而其它线程不能在我们检查条件时改变这个队列。如果队列满了,那么PRODUCER线程会在CONSUMER线程消耗掉队列里的任意一个整数,并用notify来通知PRODUCER线程之前持续等待。在我们的例子中,wait和notify都是使用在同一个共享对象上的。












demo: import java.util.LinkedList; import java.util.Queue; import java.util.Random; /** * * @ClassName: Product * @Description: 生产者线程类 * @author: xfzhong * @date: 2017年6月28日 */ class Product extends Thread{ Queue<Integer> queue;//队列定义,存放数据 int maxSize=0;//容量声明 public Product(Queue<Integer> queue,int maxSize,String name) {//创建线程对象构造函数 super(name); this.queue=queue; this.maxSize=maxSize; } @Override public void run() { while(true){ synchronized(queue){ while(queue.size()==maxSize){ try { System.out.println("队列已经满了!生产者在等待消费者消耗!"); queue.wait(); } catch (Exception e) { e.printStackTrace(); } } Random random=new Random(); int a=random.nextInt(); System.out.println("Product value:"+a); queue.add(a); queue.notifyAll();//队列满了时通知消费者线程开始执行 } } } } /** * * @ClassName: Customer * @Description: 消费者线程类 * @author: xfzhong * @date: 2017年6月28日 */ class Customer extends Thread{ Queue<Integer> queue;//队列定义,存放数据 int maxSize=0;//容量声明 public Customer(Queue<Integer> queue,int maxSize,String name) {//创建线程对象构造函数 super(name); this.queue=queue; this.maxSize=maxSize; } @Override public void run() { while(true){ synchronized (queue) { while(queue.isEmpty()){ try { System.out.println("队列为空!消费者在等待生产者生产!"); queue.wait(); } catch (Exception e) { e.printStackTrace(); } } System.out.println("customer value:"+queue.remove()); queue.notifyAll(); } } } } public class ProductOrCustomer { public static void main(String[] args) { //创建线程对象 System.out.println("start-----"); Queue<Integer> queue=new LinkedList<Integer>(); int maxSize=10; Product product=new Product(queue, maxSize, "product"); Customer customer=new Customer(queue, maxSize, "customer"); product.start(); customer.start(); System.out.println("end-----"); } }

  

 

原文地址:https://www.cnblogs.com/ipetergo/p/7091388.html