在java中,线程间的通讯可使用wait、notify、notifyAll来进行控制。从名字就能够看出来这3个方法都是跟多线程相关的,可是可能让你感到吃惊的是:这3个方法并非Thread类或者是Runnable接口的方法,而是Object类的3个本地方法。java
其实要理解这一点也并不难,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在做用等同于synchronized(obj){......}的内部才可以去调用obj的wait与notify/notifyAll三个方法,不然就会报错:多线程
java.lang.IllegalMonitorStateException:current thread not owner
也就是说,在调用这3个方法的时候,当前线程必须得到这个对象的锁,那么这3个方法就是和对象锁相关的,因此是属于Object的方法而不是Thread,由于不是每一个对象都是Thread。因此咱们在理解wait、notify、notifyAll以前,先要了解如下对象锁。ide
多个线程都持有同一个对象的时候,若是都要进入synchronized(obj){......}的内部,就必须拿到这个对象的锁,synchronized的机制保证了同一时间最多只能有1个线程拿到了对象的锁,以下图:this
3个线程竞争对象A锁线程
下面咱们来看一下这3个方法的做用:
wait:线程自动释放其占有的对象锁,并等待notify
notify:唤醒一个正在wait当前对象锁的线程,并让它拿到对象锁
notifyAll:唤醒全部正在wait前对象锁的线程code
notify和notifyAll的最主要的区别是:notify只是唤醒一个正在wait当前对象锁的线程,而notifyAll唤醒全部。值得注意的是:notify是本地方法,具体唤醒哪个线程由虚拟机控制;notifyAll后并非全部的线程都能立刻往下执行,它们只是跳出了wait状态,接下来它们还会是竞争对象锁。对象
下面经过一个经常使用生产者、消费者的例子来讲明。
消息实体类:接口
package com.podongfeng; /** * Title: Message.class<br> * Description: 消息实体<br> * Create DateTime: 2016年04月17日 下午1:27 <br> * * @author podongfeng */ public class Message { }
生产者:ip
package com.podongfeng; import java.util.ArrayList; import java.util.List; /** * Title: Producer.class<br> * Description: 消息生产者<br> * Create DateTime: 2016年04月17日 下午1:28 <br> * * @author podongfeng */ public class Producer extends Thread { List<Message> msgList = new ArrayList<>(); @Override public void run() { try { while (true) { Thread.sleep(3000); Message msg = new Message(); synchronized(msgList) { msgList.add(msg); msgList.notify(); //这里只能是notify而不能是notifyAll,不然remove(0)会报java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 } } } catch (Exception e) { e.printStackTrace(); } } public Message waitMsg() { synchronized(msgList) { if(msgList.size() == 0) { try { msgList.wait(); } catch(InterruptedException e) { e.printStackTrace(); } } return msgList.remove(0); } } }
消费者:rem
package com.podongfeng; /** * Title: Consumer.class<br> * Description: 消息消费者<br> * Create DateTime: 2016年04月17日 下午1:28 <br> * * @author podongfeng */ public class Consumer extends Thread { private Producer producer; public Consumer(String name, Producer producer) { super(name); this.producer = producer; } @Override public void run() { while (true) { Message msg = producer.waitMsg(); System.out.println("Consumer " + getName() + " get a msg"); } } public static void main(String[] args) { Producer p = new Producer(); p.start(); new Consumer("Consumer1", p).start(); new Consumer("Consumer2", p).start(); new Consumer("Consumer3", p).start(); } }
消费者线程调用waitMsg去获取一个消息实体,若是msgList为空,则线程进入wait状态;生产这线程每隔3秒钟生产出体格msg实体并放入msgList列表,完成后,调用notify唤醒一个消费者线程去消费。
最后再次提醒注意:
wait、notify、notifyAll并非Thread类或者是Runnable接口的方法,而是Object类的3个本地方法。
在调用这3个方法的时候,当前线程必须得到这个对象的锁
做者:破东风CAFEBABY 连接:https://www.jianshu.com/p/f7d4819b7b24 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。