利用wait()和notify()实现任务间的协同合作其实是一种非常低级的方式,我们还可以使用同步队列来解决任务协作问题,BlockingQueue在任何时刻都只允许一个任务插入或者移除队列,如果消费者试图从队列中获取元素,而此时该队列为空,那么队列可挂起消费者,当队列内有元素时再恢复消费者任务。比wait()和notify()更简单好用。
有这么一个事件,需要制作吐司供顾客食用,而吐司的制作分三个步骤:生产吐司、给吐司抹黄油、给抹了黄油吐司抹果酱,下面用BlockingQueue的实现类LinkedBlockingQueue来运行这个过程:
定义吐司队列:
public class ToastQueue<Toast> extends LinkedBlockingQueue<Toast> {
}
定义吐司类,用枚举值实现:
public class Toast {
public enum Status{//制作土司的三个状态
DRY,BUTTER,JAMMED
}
private Status status=Status.DRY;
private final int id;
public Toast(int id) {
this.id=id;
}
public void butter() {//上黄油
status=Status.BUTTER;
}
public void jam() {//上果酱
status=Status.JAMMED;
}
public Status getStatus() {
return status;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "Toast " + id + ":"+status;
}
}
生产吐司的线程:
public class Toaster implements Runnable {//生产土司线程
private ToastQueue<Toast> toastQueue;//生产土司的队列
private int count=0;
private Random rand=new Random(47);
public Toaster(ToastQueue<Toast> toastQueue) {
this.toastQueue = toastQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(100+rand.nextInt(500));
Toast t=new Toast(count++);//制作土司
System.out.println(t);
toastQueue.put(t);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("土司制作完毕");
}
}
抹黄油的线程:
public class Butterer implements Runnable {//抹黄油线程
private ToastQueue<Toast> dryQueue,butteredQueue;//获取土司队列和抹了黄油队列
public Butterer(ToastQueue<Toast> dryQueue, ToastQueue<Toast> butteredQueue) {
this.dryQueue = dryQueue;
this.butteredQueue = butteredQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(!Thread.interrupted()) {
Toast t=dryQueue.take();//从土司队列中获取生产好的土司,没有就等待
t.butter();//取出后则上黄油
System.out.println(t);
butteredQueue.put(t);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("黄油添加完毕");
}
}
抹果酱的线程:
public class Jammer implements Runnable {//抹果酱线程
private ToastQueue<Toast> butteredQueue,finishedQueue;//抹了黄油队列和制作完成土司队列
public Jammer(ToastQueue<Toast> butteredQueue, ToastQueue<Toast> finishedQueue) {
this.butteredQueue = butteredQueue;
this.finishedQueue = finishedQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(!Thread.interrupted()) {
Toast t=butteredQueue.take();//从黄油队列中获取抹好黄油的土司,没有就等待
t.jam();//取出后则上果酱
System.out.println(t);
finishedQueue.put(t);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("果酱添加完毕");
}
}
吃(消费)吐司的线程:
public class Eater implements Runnable {//吃(消费)生产好的土司的线程
private ToastQueue<Toast> finishedQueue;//生产好的土司队列
private int counter=0;
public Eater(ToastQueue<Toast> finishedQueue) {
this.finishedQueue = finishedQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(!Thread.interrupted()) {
Toast t=finishedQueue.take();//从制作好的土司队列中获取土司,没有就等待
if(t.getId()!=counter++||t.getStatus()!=Toast.Status.JAMMED) {
System.out.println("error:"+t);
System.exit(1);
}else System.out.println(t);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("吃完了!!!");
}
}
测试Demo:
public class ToastOMatic {
public static void main(String[] args) {
// TODO Auto-generated method stub
ToastQueue<Toast> dryQueue=new ToastQueue<Toast>();
ToastQueue<Toast> butteredQueue=new ToastQueue<Toast>();
ToastQueue<Toast> finishedQueue=new ToastQueue<Toast>();
ExecutorService exec=Executors.newCachedThreadPool();
exec.execute(new Toaster(dryQueue));
exec.execute(new Jammer(butteredQueue,finishedQueue));
exec.execute(new Eater(finishedQueue));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}
在这里,多线程间的通信没有用到任何Lock对象或者synchronized关键字的同步,同步由队列所隐式地管理了,每个生产的Toast在任何时刻都只有一个线程在操作,通过队列的阻塞,使得整个处理过程能自动地挂起和恢复运行,过程非常地清晰。
同步队列还可用于异步消息处理机制中,把需要异步的事件存入同步队列,在多线程内挂起、取出事件进行处理。