线程作为程序内部的多个执行流,相互之间是可以通讯的。线程间通讯可以通过多种方式来进行,例如:线程间可以共享变量来进行通讯,使每个线程根据共享变量的值进行操作和运算,当通过共享变量进行通讯时,通常需要引入同步控制,
线程间也可以通过 wati()、notify()和notifyAll()等方法进行通讯。
每一个类的对象的实例都有一个等待集合,当在该实例上调用wait()方法后,线程都会进入到该实例的等待集合中。
wait()、notify() 和 notifyAll()方法使类Object中定义的方法,由于Java中的类都是Object类的子类,因此,java语言中任何类都可以调用这些方法,但这些方法更多的使在多线程环境中使用。
wait()方法:
wait()方法的调用的一般形式是:
对象名.wait();
称作线程在对象上的等待,作用是把当前的线程放入对象的等待集合中。
wait()方法通常需要放入以synchronized()方法修饰的语句块或方法中,如果在synchronized外部调用wait()方法,运行时刻Java虚拟机会抛出IllegalMonitorStateException异常。
wait()方法通常被放到try{}catch() 语句块中,例如:
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
当线程调用wait()方法后,Java虚拟机会让当前的线程进入到休眠状态,并释放对对象的同步锁的控制权,允许其他线程执行该同步代码,要唤醒该线程,需要在同一个对象上调用notify()或notifyAll()方法。
notify()方法:
线程不能一直在等待集合中,必须有方法对其进行唤醒,notify()方法可以对线程进行唤醒。
notify()方法调用的一般形式如下:
对象名.notify();
当使用当前对象时,使用this作为当前对象的引用,故可以直接写成notify();
当使用某个对象的notify()方法时,将从该对象的等待集合中选择一个等待的线程唤醒,唤醒的线程将从等待集合中删除。
notifyAll()方法:
notifyAll()方法会将所有在等待集合中的线程唤醒,但由于所有的被唤醒的线程仍然要去争用synchronized锁,而synchronized锁具有排他性,最终只有一个线程获得该锁,进行执行状态,其他线程仍要继续等待。
notifyAll()方法调用一般形似如下:
对象名notifyAll();
当使用当前对象时,使用this作为当前都对象的引用,故可以直接写成notifyAll();
notify() 和notifyAll()方法不需要放入try...catch...语句中,主要的区别是:notify是唤醒一个线程,而notifyAll()是唤醒该对象等待集合中的所有线程。
当只有一个线程等待,另一个线程通知时,建议使用notify(),当有多个线程等待时,建议使用notifyAll()。
Demo示例:
模拟单缓冲区的生产者和消费者问题。
分析:单缓冲区是指生产者和消费者之间只有一个缓冲区,故生产者生产一个数据后,即进入等待状态,直到消费者消费,消费者消费一个数据后同样进入等待状态,知道生产者生产数据。
//CubbyHole.java 作为一个可操作的对象(共享区域),需要定义 共享变量 和 缓冲区,通过共享变量的值进行操作 缓冲区
public class CubbyHole{
private int goods;
private boolean empty;
public CubbyHole( ){
empty=true;
}
//从缓冲区取数据
public synchronized int get(){
while(empty){
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("消费者拿走了物品"+ goods );
empty =true;
//此时唤醒生产者线程,抓紧生产
notify();
return goods;
}
//向缓冲区放入数据
public synchronized void put(int value){
while(!empty){
try{
//有产品时,就不需要生产,不需要放数据,线程等待
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
goods = value;
System.out.println("生产者生产了物品"+goods);
empty=false;
notify();
}
}
//生产者Producer
public class Producer extends Thread{
private CubbyHole cubbyHole;
public Producer(CubbyHole c){
this.cubbyHole =c;
}
public void run(){
for(int i=0;i<50;i++){
cubbyHole.put((int)(100*Math.random()));
}
}
}
//消费者Consumer
public class Consumer extends Thread{
private CubbyHole cubbyHole;
public Consumer( CubbyHole c){
this.cubbyHole =c;
}
public void run(){
for(int i=0;i<50;i++){
cubbyHole.get();
}
}
}
//启动类
public class Index{
public static void main(String [] args){
CubbyHole c= new CubbyHole();
Producer producer = new Producer(c);
producer.start();
Consumer consumer = new Consumer(c);
consumer.start();
}
}