java 生产者 消费者 以及wait()和notify()分析
生产者消费者 模型几乎是所有操作系统课程上都会提到的一个模型,它不光可以说明操作系统的信号量机制, 可以拿来说明多线程的一些工作机制。
下面的代码是我在看这个 Java面试题目时自己搜了搜,然后结合代码中一些参考 写的一个生产者消费者的模型。
关于下面的代码,有以下几点得提前强调下:
wait()方法在官网有如下说明
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object
由代码的中stackoverflow上的这个问题 下面的回答,已经一些相应的连接可以知道,wait()会在没有任何notify的情况下自己awake,所以在检查状态时,一定要用while 而不是if,否则会错误的进入,然后导致出错。还有一种情况就是可能有较多的thread在wait,那么当有notifyAll()调用时, 所有的wait都会awake,这个时候如果大家都真的awake,势必会引发错误,所以正确的做法还是要醒来后继续检查条件。
notifyAll()在官网的简介如下
Wakes up all threads that are waiting on this object's monitor.
这里的this object's
指的当前调用notifyAll()函数的对象,即本例中的Msg的实例。notifyAll()会给所有Msg的正在wait的实例
发送awake信号,这里就更需要上面的while(而不是if)了,否则就会出错。比如有三个消费者在wait,但是目前只有一个生产者生产了
一个消息,而它就去notifyAll,那肯定就会出错。
为了更好的表示每个线程的wait和awake,我在每一个wait()后面添加了线程被唤醒时的打印,下面是某次执行的输出:
get wait
get wait
+++++++[p]++++++++
get awake
-------[]-------
get awake
get wait
+++++++[p]++++++++
get awake
-------[]-------
get wait
+++++++[p]++++++++
get awake
-------[]-------
get wait
+++++++[p]++++++++
get awake
-------[]-------
+++++++[p]++++++++
-------[]-------
+++++++[p]++++++++
-------[]-------
+++++++[p]++++++++
+++++++[p, p]++++++++
-------[p]-------
因为故意在main函数中先启动了两个消费者Consumer,所以最初可以看到两个get wait(前两行), 紧接着生产者生产了一条数据(第三行),并发送了notifyAll消息,于是两个消费者都醒了(第4、6行), 但是只有一个消费者成功的获取了消息(打印了第五行),另一个消费者醒了发现没东西,遍继续睡觉了(第七行)。 后面以此类推。
好了,Talk is cheap, 看代码吧。
package interview;
import java.lang.*;
import java.lang.Override;
import java.util.ArrayList;
/**
* User: shellbye.com@gmail.com
* Date: 2015/3/23
* ref:http://www.programcreek.com/2009/02/notify-and-wait-example/
*/
public class WaitAndNotify0 {
public static void main(String[] args) throws Exception {
Msg msg = new Msg();
Producer0 p = new Producer0(msg);
Consumer0 c = new Consumer0(msg);
p.start();
c.start();
}
}
class Msg {
ArrayList<String> m = new ArrayList<>();
public synchronized void putMsg(String msg) {
try {
// changed from if to while by this
// ref:http://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java
while (m.size() > 10) {
System.out.println("put wait");
wait();
System.out.println("put awake");
}
m.add(msg);
// when delete oen of the notifyAll() or delete both of them,,
// the program dead with 'put wait' and 'get wait'
notifyAll();
System.out.println("+++++++" + m + "++++++++");
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void getMsg() {
try {
while (m.size() < 1) {
System.out.println("get wait");
wait();
System.out.println("get awake");
}
m.remove(0);
notifyAll();
System.out.println("-------" + m + "-------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Producer0 extends Thread {
Msg m;
Producer0(Msg m) {
this.m = m;
}
@Override
public void run() {
try {
while (true) {
sleep((int) (Math.random() * 1000));
m.putMsg("p");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Consumer0 extends Thread {
Msg m;
Consumer0(Msg m) {
this.m = m;
}
@Override
public void run() {
try {
while (true) {
sleep((int) (Math.random() * 1000));
m.getMsg();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
一些参考:
[0].http://javarevisited.blogspot.sg/2012/02/why-wait-notify-and-notifyall-is.html
[1].http://javarevisited.blogspot.sg/2011/05/wait-notify-and-notifyall-in-java.html
[2].http://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java
[3].http://stackoverflow.com/questions/3362303/whats-a-monitor
blog comments powered by Disqus