1. 程式人生 > >基礎篇:執行緒間的協作之生產者與消費者(十)

基礎篇:執行緒間的協作之生產者與消費者(十)

生產者與消費者的程式碼實現,這個套路基本上和上篇文章一個樣,所以我就不對程式碼多做解釋了;

之所以寫這篇文章是為了介紹ReentrantLock 以及方便後面的一篇採用JDK提供的佇列來實現生產者與消費者的程式碼來與之做比較;

先看輸出********************************************************************************

開始做食物...
食物做完了!...
開始吃食物...
食物吃完了!...
開始做食物...
食物做完了!...
開始吃食物...
食物吃完了!...
開始做食物...
食物做完了!...
開始吃食物...
食物吃完了!...
開始做食物...

******************************************************************************************

//餐廳
class Restaurant{
	public Food food = null;//餐廳裡的食物預設是沒有的
	public Provider provider = null;
	public Consumer comsumer = null;

	public void setProvider( Provider provider){
		this.provider= provider;
	}
	public void setConsumer( Consumer consumer){
		this.comsumer= consumer;
	}
	
	//用單例,保證所有顧客都是在同一家餐廳
	private Restaurant() {}
	private static final Restaurant restaurant = new Restaurant();
	public static Restaurant getInstance(){return restaurant;}
	
}

//食物提供者(廚師)
class Provider{
	//在這家餐廳工作
	private Restaurant res =  null;
	
	public Provider( Restaurant res ) {
		this.res = res;
	}
	
	public void fry() throws InterruptedException{//炒菜
		while( !Thread.currentThread().isInterrupted() ){
			synchronized (this) {
				//如果當前還有食物沒有被消費完,則繼續等待
				while( res.food!=null ){
					wait();
				}
			}
			
			synchronized (res.comsumer) {
				System.out.println("開始做食物...");
				TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
				res.food= new Food();
				System.out.println("食物做完了!...");
				//讓顧客開始吃食物
				res.comsumer.notifyAll();
			}
		}
	}
}

//食物消費者(顧客)
class Consumer{
	//在這家餐廳就餐
	private Restaurant res = null;
	
	public Consumer( Restaurant res ) {
		this.res = res;
	}
	//吃食物方法
	public void eat() throws InterruptedException{
		while( !Thread.currentThread().isInterrupted() ){
			synchronized (this) {
				//如果當前沒有食物可用,則繼續等待
				while(res.food==null){
					wait();
				}
			}
			synchronized (res.provider) {
				System.out.println("開始吃食物...");
				TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));//吃食物時間
				res.food=null;
				System.out.println("食物吃完了!...");
				//讓廚師繼續做食物
				res.provider.notifyAll();
			}
		}
	}
}
//經典生產者與消費者演示(不採用佇列的普通實現)
	//考慮這樣一個場景:顧客在餐廳點菜後,廚師開始炒菜,廚師炒完菜後送給顧客吃,顧客吃完又通知廚師炒菜...周而復始...
	//這個場景就是典型的生產者(廚師)與消費者(顧客)
	public static void producer(){
		ExecutorService exec = Executors.newCachedThreadPool();
		Restaurant res = Restaurant.getInstance();
		final Consumer consumer = new Consumer(res);
		final Provider provider = new Provider(res);
		res.setProvider(provider);
		res.setConsumer(consumer);
		
		//啟動顧客執行緒,顧客要消費
		exec.execute(new Runnable() {
			public void run() {
				try {
					consumer.eat();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		//啟動廚師執行緒,廚師要生產
		exec.execute(new Runnable() {
			public void run() {
				try {
					provider.fry();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
	}


Ok,不做程式碼解釋,和上篇一樣,接下來我們主要看下怎麼利用ReentrantLock來實現生產者與消費者,讓我們修改下程式碼:

//餐廳
class Restaurant{
	public Food food = null;//餐廳裡的食物預設是沒有的
	public Provider provider = null;
	public Consumer comsumer = null;
//	演示變體,利用Lock來進行協作,需要增加兩把鎖以及兩個條件
	public ReentrantLock providerLock = new ReentrantLock();
	public ReentrantLock consumerLock = new ReentrantLock();
	public Condition providerCondition = providerLock.newCondition();
	public Condition consumerCondition = consumerLock.newCondition();
	
	public void setProvider( Provider provider){
		this.provider= provider;
	}
	public void setConsumer( Consumer consumer){
		this.comsumer= consumer;
	}
	
	//用單例,保證所有顧客都是在同一家餐廳
	private Restaurant() {}
	private static final Restaurant restaurant = new Restaurant();
	public static Restaurant getInstance(){return restaurant;}
	
}


//食物提供者(廚師)
class Provider{
	//在這家餐廳工作
	private Restaurant res =  null;
	
	public Provider( Restaurant res ) {
		this.res = res;
	}
	
	public void fry() throws InterruptedException{//炒菜
		while( !Thread.currentThread().isInterrupted() ){
//			開始演示使用Lock來進行協作
			res.providerLock.lock();
			while( res.food!=null ){
				res.providerCondition.await();
			}
			res.providerLock.unlock();
			
			res.consumerLock.lock();
			System.out.println("開始做食物...");
			TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
			res.food= new Food();
			System.out.println("食物做完了!...");
			//讓顧客開始吃食物
			res.consumerCondition.signalAll();
			res.consumerLock.unlock();
		}
	}
}

//食物消費者(顧客)
class Consumer{
	//在這家餐廳就餐
	private Restaurant res = null;
	
	public Consumer( Restaurant res ) {
		this.res = res;
	}
	//吃食物方法
	public void eat() throws InterruptedException{
		while( !Thread.currentThread().isInterrupted() ){
//			開始演示使用Lock來進行協作
			res.consumerLock.lock();
			while(res.food==null){
				res.consumerCondition.await();
			}
			res.consumerLock.unlock();
			
			res.providerLock.lock();
			System.out.println("開始吃食物...");
			TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));//吃食物時間
			res.food=null;
			System.out.println("食物吃完了!...");
			//讓廚師繼續做食物
			res.providerCondition.signalAll();
			res.providerLock.unlock();
		}
	}
}

稍作解釋:這裡主要利用ReentrantLock的 lock()與unLock() 來進行加解鎖 , 通過呼叫它的newCondition()方法來獲得一個物件監視器,該監視器擁有await(0與signalAll()等方法, 和Object的wait()以及notifyALL()的功能是一樣的!

多執行緒的基礎篇到這裡就結束了,你要知道的是,在實際工作中,肯定是不需要你自己來寫什麼notify和wait之類的,這太困難了,得寫一堆難看的程式碼,那麼接下來,我們就來學學怎麼使用JDK中的一些好用的工具類,來幫助我們輕而易舉的實現複雜的多執行緒程式設計問題;