Java中wait()和notify()方法的使用

时间:2020-12-06 15:35:40

1. wait方法和notify方法

这两个方法,包括notifyAll方法,都是Object类中的方法。在Java API中,wait方法的定义如下:

public final void wait()
                throws InterruptedException
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }
 
This method should only be called by a thread that is the owner of this object's monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.

在 Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信。线程在运行的时候,如果发现某些条件没有被满足,可以调用wait方法暂停自己的执行,并且放弃已经获得的锁,然后进入等待状态。当该线程被其他线程唤醒并获得锁后,可以沿着之前暂停的地方继续向后执行,而不是再次从同步代码块开始的地方开始执行。但是需要注意的一点是,对线程等待的条件的判断要使用while而不是if来进行判断。这样在线程被唤醒后,会再次判断条件是否正真满足。

想象一下一个生产者,多个消费者的场景。一个消费者Consumer1了最后一个元素,并且唤醒了其他线程,如果被唤醒的正好是Consumer2,那么此时是没有元素可以消费的。如果用的是if判断,那么被唤醒后就不会再次进行条件的判断,而是直接向下执行导致运行错误。我们的代码如下:

notify方法会唤醒等待一个对象锁的线程,但是具体唤醒哪个是不确定的。

 

2. 实现生产者消费者

如下使用if来判断会导致程序出错

Java中wait()和notify()方法的使用

一定要使用while来进行判断线程的等待条件而不是使用if

  1 package thread.learn;
  2 
  3 import java.util.LinkedList;
  4 import java.util.Queue;
  5 import java.util.Random;
  6 
  7 /**
  8  * Created by liujinhong on 2017/4/2.
  9  * 生产者消费者问题是一个很经典的问题,值得好好研究一下
 10  * java的wait和notify方法在使用时也是要非常注意的
 11  */
 12 public class ProducerConsumer {
 13     public static class Producer extends Thread {
 14         Queue<Integer> queue;
 15         int maxsize;
 16 
 17         Producer(Queue<Integer> queue, int maxsize, String name) {
 18             this.queue = queue;
 19             this.maxsize = maxsize;
 20             this.setName(name);
 21         }
 22         @Override
 23         public void run() {
 24             while (true) {
 25                 synchronized (queue) {
 26                     try{
 27                         Thread.sleep(500);
 28                     } catch (Exception e) {}
 29 
 30                     System.out.println(this.getName() + "获得队列的锁");
 31                     //条件的判断一定要使用while而不是if
 32                     if (queue.size() == maxsize) {
 33                         System.out.println("队列已满,生产者" + this.getName() + "等待");
 34                         try {
 35                             queue.wait();
 36                         } catch (Exception e) {}
 37                     }
 38                     int num = (int)(Math.random()*100);
 39                     queue.offer(num);
 40 
 41                     System.out.println(this.getName() + "生产一个元素:" + num);
 42                     queue.notifyAll();
 43 
 44                     System.out.println(this.getName() + "退出一次生产过程!");
 45                 }
 46             }
 47         }
 48     }
 49 
 50     public static class Consumer extends Thread {
 51         Queue<Integer> queue;
 52         int maxsize;
 53 
 54         Consumer(Queue<Integer> queue, int maxsize, String name) {
 55             this.queue = queue;
 56             this.maxsize = maxsize;
 57             this.setName(name);
 58         }
 59 
 60         @Override
 61         public void run() {
 62             while (true) {
 63                 synchronized (queue) {
 64                     try{
 65                         Thread.sleep(500);
 66                     } catch (Exception e) {}
 67 
 68                     System.out.println(this.getName() + "获得队列的锁");
 69                     //条件的判断一定要使用while而不是if
 70                     if (queue.isEmpty()) {
 71                         System.out.println("队列为空,消费者" + this.getName() + "等待");
 72                         try{
 73                             queue.wait();
 74                         } catch (Exception e) {}
 75                     }
 76                     int num = queue.poll();
 77                     System.out.println(this.getName() + "消费一个元素:"+num);
 78                     queue.notifyAll();
 79 
 80                     System.out.println(this.getName() + "退出一次消费过程!");
 81                 }
 82             }
 83         }
 84     }
 85 
 86     public static void main(String[] args) {
 87         Queue<Integer> queue = new LinkedList<>();
 88         int maxsize = 2;
 89 
 90         Producer producer = new Producer(queue, maxsize, "Producer");
 91         Consumer consumer1 = new Consumer(queue, maxsize,"Consumer1");
 92         Consumer consumer2 = new Consumer(queue, maxsize,"Consumer2");
 93         Consumer consumer3 = new Consumer(queue, maxsize,"Consumer3");
 94 
 95         producer.start();
 96         consumer1.start();
 97         consumer2.start();
 98         consumer3.start();
 99     }
100 }