Java多线程基础(8):生产者-消费者和队列

利用wait()和notify()实现任务间的协同合作其实是一种非常低级的方式,我们还可以使用同步队列来解决任务协作问题,BlockingQueue在任何时刻都只允许一个任务插入或者移除队列,如果消费者试图从队列中获取元素,而此时该队列为空,那么队列可挂起消费者,当队列内有元素时再恢复消费者任务。比wait()和notify()更简单好用。
有这么一个事件,需要制作吐司供顾客食用,而吐司的制作分三个步骤:生产吐司、给吐司抹黄油、给抹了黄油吐司抹果酱,下面用BlockingQueue的实现类LinkedBlockingQueue来运行这个过程:
定义吐司队列:

public class ToastQueue extends LinkedBlockingQueue {
}

定义吐司类,用枚举值实现:

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 toastQueue;//生产土司的队列
	private int count=0;
	private Random rand=new Random(47);

	public Toaster(ToastQueue 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 dryQueue,butteredQueue;//获取土司队列和抹了黄油队列

	public Butterer(ToastQueue dryQueue, ToastQueue 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 butteredQueue,finishedQueue;//抹了黄油队列和制作完成土司队列

	public Jammer(ToastQueue butteredQueue, ToastQueue 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 finishedQueue;//生产好的土司队列
	private int counter=0;
	
	public Eater(ToastQueue 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 dryQueue=new ToastQueue();
		ToastQueue butteredQueue=new ToastQueue();
		ToastQueue finishedQueue=new ToastQueue();
		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在任何时刻都只有一个线程在操作,通过队列的阻塞,使得整个处理过程能自动地挂起和恢复运行,过程非常地清晰。
同步队列还可用于异步消息处理机制中,把需要异步的事件存入同步队列,在多线程内挂起、取出事件进行处理。

你可能感兴趣的:(后端开发)