執行緒間的協作(2)——生產者與消費者模式

江左煤郎發表於2018-08-31

1.何為生產者與消費者

    線上程世界裡,生產者就是生產資料的執行緒,消費者就是消費資料的執行緒。


import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName:Restraurant
 * @Description:何為生產者與消費者
 * @author: 
 * @date:2018年5月3日
 */
public class Restraurant {
	Meal m=null;
	Chef chef=new Chef(this);
	WaitPerson wait=new WaitPerson(this);
	ExecutorService service=Executors.newCachedThreadPool();
	public Restraurant() {
		service.execute(chef);
		service.execute(wait);
	}
	public static void main(String[] args) {
		new Restraurant();
	}
}
/**
 * @ClassName:Meal
 * @Description:生產者生成的資料
 * @author: 
 * @date:2018年5月3日
 */
class Meal{
	private final int orderNum;//食物訂單編號
	public Meal(int num){
		orderNum=num;
	}
	public String toString(){
		return "Meal"+orderNum;
	}
}
/**
 * @ClassName:Chef
 * @Description:廚師類,及生產者
 * @author: 
 * @date:2018年5月3日
 */
class Chef implements Runnable{
	Restraurant r;
	int count=0;
	public Chef(Restraurant r) {
		this.r=r;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				synchronized (this) {
					while(r.m!=null){
						System.out.println("廚師等待中");
						wait();//等待服務員取餐
					}
				}
				if(count++==10){
					System.out.println("今日已售完");
					r.service.shutdownNow();
				}
				System.out.println("訂單完成,服務員取餐");
				synchronized (r.wait) {
					r.m=new Meal(count);
					r.wait.notifyAll();
					
				}
				TimeUnit.SECONDS.sleep(1);
			}
		}catch (InterruptedException e) {
			System.out.println("生產者執行緒強制中斷");
		}
		
	}
}
/**
 * @ClassName:WaitPerson
 * @Description:服務員類,即消費者
 * @author: 
 * @date:2018年5月3日
 */
class WaitPerson implements Runnable{
	Restraurant r;
	public WaitPerson(Restraurant r) {
		this.r=r;
	}
	@Override
	public void run() {
		try {
			while (!Thread.interrupted()) {
				synchronized (this) {
					while (r.m == null) {
						System.out.println("服務員等待中");
						wait();// 等待廚師生成食物
					}
				}

				System.out.println("服務員以取餐" + r.m);
				synchronized (r.chef) {
					r.m = null;
					r.chef.notifyAll();
				}
			}
		} catch (InterruptedException e) {
			System.out.println("消費者執行緒強制中斷");
		}
		
	}
	
}

2.生產者與消費者模式

    1)產生原因:在多執行緒開發 中,如果生產者處理速度很快,而消費者處理速度很慢,那麼生產者就必須等待消費者處理 完,才能繼續生產資料。同樣的道理,如果消費者的處理能力大於生產者,那麼消費者就必須 等待生產者。wait與notify方法以一種非常低階的方式解決了任務互相通知的問題,即每次互動都要進行一次握手,極大影響的效率以及效能,為了解決這種生產消費能力不均衡的問題,便有了生產者和消費者模式。

    2)原理:生產者和消費者模式是通過一個容器(比如同步阻塞佇列)來解決生產者和消費者的強耦合問題。生產者和消 費者彼此之間不直接通訊,而是通過阻塞佇列來進行通訊,所以生產者生產完資料之後不用 等待消費者處理,直接扔給阻塞佇列,消費者不找生產者要資料,而是直接從阻塞佇列裡取, 阻塞佇列就相當於一個緩衝區,平衡了生產者和消費者的處理能力。 這個阻塞佇列就是用來給生產者和消費者解耦的。java.util.concurrent.BlockingQueue介面提供了這個佇列,通常使用其實現子類ArrayBlockingQueue,LinkedBlockingQueue。當消費者任務試圖從同步佇列中獲取物件,如果佇列為空時,那麼佇列則會掛起消費者任務,並且當擁有足夠多的元素可用時才會恢復消費者任務。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class UseBlockingQueue {
	public static void main(String[] args) throws InterruptedException {
		LinkedBlockingQueue<Toast> dry=new LinkedBlockingQueue<Toast>(),
				butter=new LinkedBlockingQueue<Toast>(),
				jam=new LinkedBlockingQueue<Toast>(),
				con=new LinkedBlockingQueue<Toast>();
		ExecutorService exec=Executors.newCachedThreadPool();
		exec.execute(new MakeToast(dry));//製作初始吐司任務
		exec.execute(new Butter(dry,butter));//吐司抹黃油任務
		exec.execute(new Jam(butter,jam));//吐司抹果醬任務
		exec.execute(new Consumer(jam));//消費者任務,食用吐司
		TimeUnit.SECONDS.sleep(5);
		exec.shutdownNow();
	}
}
class Toast{
	private int status;//吐司狀態:0代表製作吐司,1代表抹黃油,2代表向抹了黃油的吐司抹果醬
	private final int id;
	public Toast(int id1) {
		id=id1;
	}
	public void butter(){
		status=1;
	};
	public void jam(){
		status=2;
	}
	public int getStatus(){
		return status;
	}
	public int getId(){
		return id;
	}
	public String toString(){
		return "toast "+id+":"+status;
	}
}
/**
 * @Description:製作初始吐司
 */
class MakeToast implements Runnable{
	private LinkedBlockingQueue<Toast> queue=new LinkedBlockingQueue<Toast>();
	private int count=0;
	public MakeToast(LinkedBlockingQueue<Toast> q) {
		queue=q;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Thread.sleep(1000);//製作時間
				Toast t=new Toast(count);
				System.out.println(t);
				queue.put(t);//新增到同步佇列
				count++;
			}
		}catch (InterruptedException e) {
			System.out.println("make process interrupted");
		}
		System.out.println("make process off");
	}
}
/**
 * @Description:塗抹黃油
 */
class Butter implements Runnable{
	private LinkedBlockingQueue<Toast> queue1,queue2;//未加料吐司佇列,抹黃油後吐司佇列
	public Butter(LinkedBlockingQueue<Toast> q1,LinkedBlockingQueue<Toast>q2) {
		queue1=q1;
		queue2=q2;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Toast t=queue1.take();//如果佇列中沒有可用元素將會阻塞,直至有可用元素被新增
				t.butter();
				System.out.println(t);
				queue2.put(t);
			}
		}catch (InterruptedException e) {
			System.out.println("butter process interrupted");
		}
		System.out.println("butter process off");
	}
}
/**
 * @Description:塗抹果醬
 */
class Jam implements Runnable{
	private LinkedBlockingQueue<Toast> queue1,queue2;//抹黃油後吐司佇列,抹果醬吐司佇列
	public Jam(LinkedBlockingQueue<Toast> q1,LinkedBlockingQueue<Toast>q2) {
		queue1=q1;
		queue2=q2;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Toast t=queue1.take();//如果佇列中沒有可用元素將會阻塞,直至有可用元素被新增
				t.jam();
				System.out.println(t);
				queue2.put(t);
			}
		}catch (InterruptedException e) {
			System.out.println("jam process interrupted");
		}
		System.out.println("jam process off");
	}
}
/**
 * @Description:被食用
 */
class Consumer implements Runnable{
	private LinkedBlockingQueue<Toast> finished;//抹黃油後吐司佇列,抹果醬吐司佇列
	int count=0;
	public Consumer(LinkedBlockingQueue<Toast> q) {
		finished=q;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Toast t=finished.take();//如果佇列中沒有可用元素將會阻塞,直至有可用元素被新增
				if(t.getId()!=count++||t.getStatus()!=2){
					System.out.println("過程出現錯誤");
					return;
				}else{
					System.out.println("所有過程正確實現"+"toast "+t.getId()+"被食用");
				}
			}
		}catch (InterruptedException e) {
			System.out.println("eat process interrupted");
		}
		System.out.println("eat process off");
	}
}

 


相關文章