线程作为程序内部的多个执行流,相互之间是可以通讯的。线程间通讯可以通过多种方式来进行,例如:线程间可以共享变量来进行通讯,使每个线程根据共享变量的值进行操作和运算,当通过共享变量进行通讯时,通常需要引入同步控制,
线程间也可以通过 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()。
Object.wait 中JDK提供的doc文档
1 2 3 4 5 6 7 8 9 10 |
|
一个对象的monitor只能被一个线程占用,wait()方法会释放这个对象的锁, 既然要释放 就先要取得这个锁, 取得对象锁的方式只有synchronized()。释放锁之后, 线程进入BLOCK状态
doc文档中说明调用wait的时机是因为运行条件condition不满足,
比如生产者没有往队列中放东西,队列已经空了。队列不为空的时候,再调用obj.notify()。此时condation==队列,obj是针对这个队列的线程间共享变量,也可以是队列本身(最好不是,会阻塞生产者的线程)?
Demo示例:
模拟单缓冲区的生产者和消费者问题。
分析:单缓冲区是指生产者和消费者之间只有一个缓冲区,故生产者生产一个数据后,即进入等待状态,直到消费者消费,消费者消费一个数据后同样进入等待状态,知道生产者生产数据。
package com.auth;
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();
}
}
例子代码:
说明:线程运行3秒后调用wait等待,6秒后主线程调用notify唤醒等待线程。
public class MyThread implements Runnable {
private Object flag;
private String threadName;
public MyThread(Object flag,String threadName) {
this.flag = flag;
this.threadName = threadName;
}
@Override
public void run(){
try{
for(int i = 0; i < 10;i++){
if(i == 3){
synchronized (this.flag){
System.out.println("3秒后线程调用wait睡眠");
this.flag.wait();
}
}
System.out.println(this.threadName + " " + i);
Thread.sleep(1000);
}
} catch(InterruptedException e){
e.printStackTrace();
}
}
}
public class TestMain {
public static void main(String[] args) {
Object object = new Object();
MyThread myThread1 = new MyThread(object,"thread1");
// MyThread myThread2 = new MyThread(object,"thread2");
Thread test1 = new Thread(myThread1);
test1.start();
// Thread test2 = new Thread(myThread1);
// test2.start();
try{
Thread.sleep(6000);
System.out.println("6秒后唤醒线程");
synchronized (object){
object.notify();
}
System.in.read();
} catch(InterruptedException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
}
结果:
测试notifyAll(MyThread代码不变)
main方法代码:
import java.io.IOException;
/**
* Created on 2016/1/31.
*/
public class TestMain {
public static void main(String[] args) {
Object object = new Object();
MyThread myThread1 = new MyThread(object,"thread1");
MyThread myThread2 = new MyThread(object,"thread2");
Thread test1 = new Thread(myThread1);
test1.start();
Thread test2 = new Thread(myThread2);
test2.start();
try{
Thread.sleep(6000);
System.out.println("6秒后唤醒线程");
synchronized (object){
object.notifyAll();
}
System.in.read();
} catch(InterruptedException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
}
结果截图: