一.等待/通知机制
1.通过wait/notify实现等待/通知机制
wait():可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒
notify():可以随机唤醒等待队列中等待同一共享资源的一个线程,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知一个线程,且随机
notifyAll():可以使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态
在多线程程序中,单个线程可能会需要满足某些逻辑条件才能继续运行。当线程所要求的条件不满足时,线程进入等待状态,等待由于其他线程的运行而使条件得到满足;其他线程则负责在合适的时机发出通知来唤醒处于等待状态的线程。对于这种场景,可以使用while循环和volatile变量来处理。但是这种做法的本质是让线程处于忙等待的状态,并通过轮询的方式来判断条件是否满足。处于忙等待的线程仍然占用CPU时间,对性能造成影响。更好的做法是使用Object类提供的wait、notify和notifyAll
例1:使用wait/notify实现等待/通知机制
public class WaitAndNotify {
static String obj="obj";
public static void main(String[] args) {
// TODO Auto-generated method stub
WaitAndNotify waitAndNotify=new WaitAndNotify();
Runnable thread1=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(obj){
System.out.println("begin wait() ThreadName="+Thread.currentThread().getName());
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("end wait() ThreadName="+Thread.currentThread().getName());
}
}
};
Runnable thread2=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (obj){
System.out.println("begin wait() ThreadName="+Thread.currentThread().getName());
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("end wait() ThreadName="+Thread.currentThread().getName());
}
}
};
(new Thread(thread1)).start();
(new Thread(thread2)).start();
synchronized(obj){
obj.notify();
}
}
}
运行结果:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
end wait() ThreadName=Thread-0
例2:使用wait/notifyAll实现等待/通知机制
将例1中的notify换成notifyAll
synchronized(obj){
obj.notifyAll();
}
运行结果:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
end wait() ThreadName=Thread-1
end wait() ThreadName=Thread-0
二.通过管道进行线程间通信
在java语言中提供了一种特殊的流,管道流,用于在不同线程间直接传送数据,一个线程发送数据到输出管道,另一个线程从输入管道中读取数据
在javaJDK中提供了4个类来使线程间可以通信:
1)字节流:PipedInputStream和PipedOutputStream
2)字符流:PipedReader和PipedWriter
1.字节流
例1:使用管道字节流实现线程通信
//写
import java.io.IOException;
import java.io.PipedOutputStream;
public class WriteData extends Thread {
PipedOutputStream out;
public WriteData(PipedOutputStream out){
this.out=out;
}
public void writeMethod(PipedOutputStream out) throws IOException{
System.out.println("write:");
for(int i=0;i<50;i++){
String outData=""+(i+1);
out.write(outData.getBytes());
System.out.print(outData);
}
out.close();
System.out.println();
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
writeMethod(out);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//读
import java.io.IOException;
import java.io.PipedInputStream;
public class ReadData extends Thread {
PipedInputStream in;
public ReadData(PipedInputStream in){
this.in=in;
}
public void readMethod(PipedInputStream in) throws IOException{
byte[] b=new byte[20];
int readLength=in.read(b);
while(readLength!=-1){
String newData=new String(b,0,readLength);
System.out.println("Read:");
System.out.println(newData);
readLength=in.read(b);
}
in.close();
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
this.readMethod(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//测试程序
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class Main {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
PipedOutputStream out=new PipedOutputStream();
PipedInputStream in=new PipedInputStream();
out.connect(in);
WriteData write=new WriteData(out);
ReadData read=new ReadData(in);
write.start();
read.start();
}
}
运行结果:
write:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
Read:
12345678910111213141
Read:
51617181920212223242
Read:
52627282930313233343
Read:
53637383940414243444
Read:
54647484950
2.字符流
只需要将PipedInputStream和PipedOutputSteam对象变成PipedReader和PIpedWriter对象即可