基礎篇:執行緒間的協作之生產者與消費者(十)
阿新 • • 發佈:2019-02-03
生產者與消費者的程式碼實現,這個套路基本上和上篇文章一個樣,所以我就不對程式碼多做解釋了;
之所以寫這篇文章是為了介紹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中的一些好用的工具類,來幫助我們輕而易舉的實現複雜的多執行緒程式設計問題;