一、什么是等待/通知机制
等待/通知机制在生活中比比皆是,比如在就餐时就会出现
厨师和服务员之间的交互要在“菜品传递台”上,在这期间会有几个问题:
1)厨师做完一道菜的时间不确定,所以厨师将菜品放到“菜品传递台”上的时间也是不确定的。
2)服务员取到菜的时间取决于厨师,所以服务员就有“等待(wait)”的状态。
3)服务员如何能取到菜呢?这又的取决于厨师,厨师将菜放在“菜品传递台”上,其实就相当于一种通知(notify),这是服务员才可以拿到菜并交给就餐者。
4)在这个过程中出现了“等待/通知”机制
二、等待/通知机制的实现
方法 wait()的作用是使当前执行代码的线程进行等待,wait()方法时Object类的方法,该方法用来将当前线程置入“预执行队列”中并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步代码块中调用wait()方法。在执行wait()方法后,当前线程释放锁。当被通知后,线程需要和其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException 的子类,所以,不需要通过try-catch 语句来进行异常捕获。
方法 notify() 也要在同步方法或同步代码块中调用,也就是线程在调用前必须获得该对象的对象级别锁。如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。该方法用于通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则有线程规划器随机挑选其中一个呈wait()状态的线程,对其发出通知notify,并使他等待获取该对象的对象锁。需要注意的是,在执行了notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象的锁,要等到执行notify()方法的线程将线程执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait转态,直到这个对象发出一个notify 或者notifyAll。
用一句话总结 wait使线程停止运行,而notify使停止的线程继续运行
1. wait() 方法与sleep()方法
wait()方法执行后当前线程会释放锁,而sleep()方法执行后不会释放当前线程的锁。
2.notify() 方法与notifyAll()方法
notify() 方法 每次只能唤醒一个线程,但是唤醒的线程是随机的,所以有可能将与自己是同一类(如生产者)的线程唤醒,造成死锁。
notifyAll()方法 是将所有的处于wait()状态下的线程都给唤醒。
3.wait(long) 方法
带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
三、通过管道流进行线程间通讯
在Java语言中提供了各种各样的输入/输出流 Stream,是我们能够很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输入管道中读取数据。通过使用管道,实现不同线程间的通信,而无须借助于类似临时文件之类的东西。
1.字节流
写代码:
import java.io.PipedOutputStream;
public class WriteData {
public void writeMethod(PipedOutputStream out){
try{
System.out.println("write :");
for(int i = 0; i < 300; i++){
String outData = "" + (i + 1);
out.write(outData.getBytes());
System.out.print(outData);
}
System.out.println();
out.close();
}catch (Exception a){
}
}
}
读代码:
import java.io.PipedInputStream;
public class ReadData {
public void readMethod(PipedInputStream input){
try {
System.out.println("read :");
byte[] byteArray = new byte[20];
int readLength = input.read(byteArray);
while(readLength !=-1){
String newData = new String(byteArray,0,readLength);
System.out.print("jinru");
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
写线程:
import java.io.PipedOutputStream;
public class ThreadWrite extends Thread{
private WriteData writedata;
private PipedOutputStream out;
public ThreadWrite(WriteData writedata,PipedOutputStream out){
this.writedata = writedata;
this.out = out;
}
@Override
public void run() {
super.run();
writedata.writeMethod(out);
}
}
读线程:
import java.io.PipedInputStream;
public class ThreadRead extends Thread{
private ReadData readdata;
private PipedInputStream input;
public ThreadRead(ReadData readdata,PipedInputStream input){
super();
this.readdata = readdata;
this.input = input;
}
@Override
public void run() {
super.run();
readdata.readMethod(input);
}
}
运行代码:
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class Run {
public static void main(String[] args){
try {
WriteData writedata = new WriteData();
ReadData readdata = new ReadData();
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outStream = new PipedOutputStream();
outStream.connect(inputStream);
ThreadWrite threadWrite = new ThreadWrite(writedata, outStream);
threadWrite.start();
Thread.sleep(2000);
ThreadRead threadread = new ThreadRead(readdata, inputStream);
threadread.start();
} catch (Exception e) {
// TODO: handle exception
}
}
}
运行结果:(未粘全)
2.字符流
PipedReader 和 PipedWriter