博客 线程间协作:wait、notify、notifyAll讲了这几个方法的使用,这篇博客举例说明。
要解决的问题是:
实现两个线程A,B,A打印5个奇数,B打印5个偶数,交替进行,如此循环50次。
要点:
1. 在同一个类中实现打印奇数和打印偶数的方法,并且这两个方法都需要被synchronized修饰,因为wait() notify()只能在synchronized修饰的代码块中运行。
2. 在类中设置一个线程执行的标志oddOrder,这个标志用来说明线程执行的次数,如果oddOrder==true,说明轮到打印奇数的线程执行了,如果为false,说明轮到打印偶数的线程的执行了。
3. 方法中一定是先调用notify(),然后再调用wait(),不然线程只执行一次(因为都进入wait状态,或标志oddOrder未修改成功)。
4. 在主函数中用同一个对象实现打印奇数和偶数的功能,因为wait()只是释放本对象的对象锁。
代码如下:
/**
* 利用wait,notify(notifyAll)实现线程间通信,一个线程打印5个奇数,另一个线程打印5个偶数
* 交替进行,直到到达100
*/
public class ThreadCom {
public static void main(String[] args) {
final PrintNum pn = new PrintNum(); //使用同一个对象执行任务
new Thread(new Runnable() { //打印奇数线程
@Override
public void run() {
for(int i=0;i<10;i++){ //执行10次
pn.printOdd();
}
System.out.println("first thread stop !");
}
}).start();
new Thread(new Runnable(){ //打印偶数线程
@Override
public void run() {
for(int i=0;i<10;i++){
pn.printEven();
}
System.out.println("second thread stop !");
}
}).start();
}
}
class PrintNum{
private int cur = 1;
private boolean printOddFlag = true; //打印odd的标志位
public synchronized void printOdd(){
//当有一个生产者和消费者时,使用if语句判断是否wait是可以的,
//当有一个生产者和两个消费者时,一个生产者,而且使用notifyAll操作,
//那么会发生产生了一个Item,而唤醒了多个消费者的情况,这个时候可以将
//if语句改为while语句
if(!printOddFlag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i=cur;i<cur+10;i+=2){
System.out.print(i+" ");
}
System.out.println();
printOddFlag = false;
this.notify(); //唤醒其他所有进程(其实只有一个)
}
public synchronized void printEven() {
if(printOddFlag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i=cur+1;i<cur+1+10;i+=2){
System.out.print(i+" ");
}
System.out.println();
cur += 10;
printOddFlag = true;
this.notify();
}
}
方法wait()和notify()都需要在同步方法或者同步代码块中调用,否则会发生IllegalMonitorStateException。在执行nofity()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。