一、官方文档
对于wait()和notify()的理解,还是要从jdk官方文档中开始,在Object类方法中有:对于wait()和notify()的理解,还是要从jdk官方文档中开始,在Object类方法中有:
void notify() Wakes up a single thread that is waiting on this object’s monitor. 译:唤醒在此对象监视器上等待的单个线程 void notifyAll() Wakes up all threads that are waiting on this object’s monitor. 译:唤醒在此对象监视器上等待的所有线程 void wait( ) Causes the current thread to wait until another thread invokes the notify() method or the notifyAll( ) method for this object. 译:导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法 void wait(long timeout) Causes the current thread to wait until either another thread invokes the notify( ) method or the notifyAll( ) method for this object, or a specified amount of time has elapsed. 译:导致当前的线程等待,直到其他线程调用此对象的notify() 方法或 notifyAll() 方法,或者指定的时间过完。 void wait(long timeout, int nanos) Causes the current thread to wait until another thread invokes the notify( ) method or the notifyAll( ) method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed. 译:导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法,或者其他线程打断了当前线程,或者指定的时间过完。
上面是官方文档的简介,下面我们根据官方文档总结一下:
- wait( ),notify( ),notifyAll( )都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。
- 当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常
- 当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。
- 在while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知
- 调用obj.wait( )释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。
- notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)
- notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)
- 假设有三个线程执行了obj.wait( ),那么obj.notifyAll( )则能全部唤醒tread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,tread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行。
- 当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行。
二、wait()和notify()的通常用法
Java多线程开发中,我们常用到wait()和notify()方法来实现线程间的协作,简单的说步骤如下:
1. A线程取得锁,执行wait(),释放锁;
2. B线程取得锁,完成业务后执行notify(),再释放锁;
3. B线程释放锁之后,A线程取得锁,继续执行wait()之后的代码;
三、生产者与消费者模式
假设有一个公共的容量有限的池子,有两种人,一种是生产者,另一种是消费者。需要满足如下条件:
1、生产者产生资源往池子里添加,前提是池子没有满,如果池子满了,则生产者暂停生产,直到自己的生成能放下池子。
2、消费者消耗池子里的资源,前提是池子的资源不为空,否则消费者暂停消耗,进入等待直到池子里有资源数满足自己的需求。
运用wait()和notify()实现如下:
抽象仓库类 public interface AbstractStorage { void consume(int num); void produce(int num); }
仓库类 /** * 生产者和消费者的问题 * wait、notify/notifyAll() 实现 */ public class Storage1 implements AbstractStorage { //仓库最大容量 private final int MAX_SIZE = 100; //仓库存储的载体 private LinkedList list = new LinkedList(); //生产产品 public void produce(int num){ //同步 synchronized (list){ //仓库剩余的容量不足以存放即将要生产的数量,暂停生产 while(list.size()+num > MAX_SIZE){ System.out.println("【要生产的产品数量】:" + num + "\t【库存量】:" + list.size() + "\t暂时不能执行生产任务!"); try { //条件不满足,生产阻塞 list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<num;i++){ list.add(new Object()); } System.out.println("【已经生产产品数】:" + num + "\t【现仓储量为】:" + list.size()); list.notifyAll(); } } //消费产品 public void consume(int num){ synchronized (list){ //不满足消费条件 while(num > list.size()){ System.out.println("【要消费的产品数量】:" + num + "\t【库存量】:" + list.size() + "\t暂时不能执行生产任务!"); try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //消费条件满足,开始消费 for(int i=0;i<num;i++){ list.remove(); } System.out.println("【已经消费产品数】:" + num + "\t【现仓储量为】:" + list.size()); list.notifyAll(); } } }
生产者 public class Producer extends Thread{ //每次生产的数量 private int num ; //所属的仓库 public AbstractStorage abstractStorage; public Producer(AbstractStorage abstractStorage){ this.abstractStorage = abstractStorage; } public void setNum(int num){ this.num = num; } // 线程run函数 @Override public void run() { produce(num); } // 调用仓库Storage的生产函数 public void produce(int num) { abstractStorage.produce(num); } }
消费者 public class Consumer extends Thread{ // 每次消费的产品数量 private int num; // 所在放置的仓库 private AbstractStorage abstractStorage1; // 构造函数,设置仓库 public Consumer(AbstractStorage abstractStorage1) { this.abstractStorage1 = abstractStorage1; } // 线程run函数 public void run() { consume(num); } // 调用仓库Storage的生产函数 public void consume(int num) { abstractStorage1.consume(num); } public void setNum(int num){ this.num = num; } }
测试类 public class Test{ public static void main(String[] args) { // 仓库对象 AbstractStorage abstractStorage = new Storage1(); // 生产者对象 Producer p1 = new Producer(abstractStorage); Producer p2 = new Producer(abstractStorage); Producer p3 = new Producer(abstractStorage); Producer p4 = new Producer(abstractStorage); Producer p5 = new Producer(abstractStorage); Producer p6 = new Producer(abstractStorage); Producer p7 = new Producer(abstractStorage); // 消费者对象 Consumer c1 = new Consumer(abstractStorage); Consumer c2 = new Consumer(abstractStorage); Consumer c3 = new Consumer(abstractStorage); // 设置生产者产品生产数量 p1.setNum(10); p2.setNum(10); p3.setNum(10); p4.setNum(10); p5.setNum(10); p6.setNum(10); p7.setNum(80); // 设置消费者产品消费数量 c1.setNum(50); c2.setNum(20); c3.setNum(30); // 线程开始执行 c1.start(); c2.start(); c3.start(); p1.start(); p2.start(); p3.start(); p4.start(); p5.start(); p6.start(); p7.start(); } }
运行结果: 【要消费的产品数量】:50 【库存量】:0 暂时不能执行生产任务! 【要消费的产品数量】:20 【库存量】:0 暂时不能执行生产任务! 【要消费的产品数量】:30 【库存量】:0 暂时不能执行生产任务! 【已经生产产品数】:10 【现仓储量为】:10 【要消费的产品数量】:30 【库存量】:10 暂时不能执行生产任务! 【要消费的产品数量】:20 【库存量】:10 暂时不能执行生产任务! 【要消费的产品数量】:50 【库存量】:10 暂时不能执行生产任务! 【已经生产产品数】:10 【现仓储量为】:20 【已经生产产品数】:10 【现仓储量为】:30 【要消费的产品数量】:50 【库存量】:30 暂时不能执行生产任务! 【已经消费产品数】:20 【现仓储量为】:10 【要消费的产品数量】:30 【库存量】:10 暂时不能执行生产任务! 【已经生产产品数】:10 【现仓储量为】:20 【要消费的产品数量】:50 【库存量】:20 暂时不能执行生产任务! 【要消费的产品数量】:30 【库存量】:20 暂时不能执行生产任务! 【已经生产产品数】:10 【现仓储量为】:30 【已经消费产品数】:30 【现仓储量为】:0 【要消费的产品数量】:50 【库存量】:0 暂时不能执行生产任务! 【已经生产产品数】:10 【现仓储量为】:10 【要消费的产品数量】:50 【库存量】:10 暂时不能执行生产任务! 【已经生产产品数】:80 【现仓储量为】:90 【已经消费产品数】:50 【现仓储量为】:40