1. 程式人生 > >java 多執行緒併發系列之 生產者消費者模式的兩種實現

java 多執行緒併發系列之 生產者消費者模式的兩種實現

生產者消費者模式是併發、多執行緒程式設計中經典的設計模式,生產者和消費者通過分離的執行工作解耦,簡化了開發模式,生產者和消費者可以以不同的速度生產和消費資料。

真實世界中的生產者消費者模式

生產者和消費者模式在生活當中隨處可見,它描述的是協調與協作的關係。比如一個人正在準備食物(生產者),而另一個人正在吃(消費者),他們使用一個共用的桌子用於放置盤子和取走盤子,生產者準備食物,如果桌子上已經滿了就等待,消費者(那個吃的)等待如果桌子空了的話。這裡桌子就是一個共享的物件。

我們看這樣一個例子:生產者:  往一個公共的盒子裡面放蘋果 消費者:從公共的盒子裡面取蘋果盒子:盒子的容量不能超過5下面我們用兩者方法分別實現這樣一個場景。
方法一:   wait()  和   notify()   通訊方法實現看盒子程式碼 publicclass PublicBox { privateint apple = 0; publicsynchronized void increace() { while (apple ==5) { try                        wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    } 
               }  apple++;  System. out .println("生成蘋果成功!" );               notify();            } publicsynchronized void decreace() { while (apple ==0) { try                        wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    } 
               } apple--; System. out.println( "消費蘋果成功!" );               notify();            } publicstatic void main(String []args)              {                     PublicBox box= new PublicBox();                     Consumer con= new Consumer(box);                     Producer pro= new Producer(box);                     Thread t1= new Thread(con);                     Thread t2= new Thread(pro);                     t1.start();                     t2.start();              }       }生產者程式碼(定義十次):publicclass Producer implements Runnable { private PublicBox boxpublic Producer(PublicBox box) { this .box = box;     } @Overridepublicvoid run() {  for( int i=0;i<10;i++)         { try {                     System. out .println("pro  i:" +i);                           Thread. sleep(30);                     } catch (InterruptedException e) { // TODO: handle exception                           e.printStackTrace();                     }box.increace();        }    } }消費者程式碼(同樣十次):publicclass Consumer implements Runnable { private PublicBox boxpublic Consumer(PublicBox box) { this .box = box;     } @Overridepublicvoid run() {  for( int i=0;i<10;i++)       { try {                     System. out .println("Con: i " +i);                           Thread. sleep(3000);                // 這裡設定跟上面30不同是為了 盒子中的蘋果能夠增加,不會生產一個馬上被消費                       } catch (InterruptedException e) { // TODO: handle exception                           e.printStackTrace();                     }box.decreace();         }  } }輸出如下:pro  i:0Con: i 0生成蘋果成功!pro  i:1生成蘋果成功!pro  i:2生成蘋果成功!pro  i:3生成蘋果成功!pro  i:4生成蘋果成功!pro  i:5消費蘋果成功!Con: i 1生成蘋果成功!pro  i:6消費蘋果成功!Con: i 2生成蘋果成功!pro  i:7消費蘋果成功!生成蘋果成功!pro  i:8Con: i 3消費蘋果成功!生成蘋果成功!pro  i:9Con: i 4消費蘋果成功!生成蘋果成功!Con: i 5消費蘋果成功!Con: i 6消費蘋果成功!Con: i 7消費蘋果成功!Con: i 8消費蘋果成功!Con: i 9消費蘋果成功!方法二:採用阻塞佇列實現生產者消費者模式阻塞佇列實現生產者消費者模式超級簡單,它提供開箱即用支援阻塞的方法put()和take(),開發者不需要寫困惑的wait-nofity程式碼去實現通訊。BlockingQueue 一個介面,Java5提供了不同的現實,如ArrayBlockingQueue和LinkedBlockingQueue,兩者都是先進先出(FIFO)順序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可選的邊界。下面這是一個完整的生產者消費者程式碼例子,對比傳統的wait、nofity程式碼,它更易於理解。盒子程式碼:import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;publicclass PublicBoxQueue { publicstatic void main(String []args)       { BlockingQueue publicBoxQueue= newLinkedBlockingQueue(5);   //定義了一個大小為5的盒子              Thread pro= new Thread(new ProducerQueue(publicBoxQueue));              Thread con= new Thread(new ConsumerQueue(publicBoxQueue));              pro.start();              con.start();       }}生產者:package dsd;import java.util.concurrent.BlockingQueue;publicclass ProducerQueue implements Runnable { privatefinal BlockingQueueproQueue; public ProducerQueue(BlockingQueue proQueue)       { this .proQueue =proQueue;       } @Override publicvoid run() { // TODO Auto-generated method stub for (int i=0;i<10;i++)              { try {                           System. out .println("生產者生產的蘋果編號為 : " +i);  //放入十個蘋果編號 為1到10 proQueue .put(i); /*Thread.sleep(3000);*/                     } catch (InterruptedException  e) { // TODO: handle exception                           e.printStackTrace();                     }              }       }}消費者:package dsd;import java.util.concurrent.BlockingQueue;publicclass ConsumerQueue implements Runnable { privatefinal BlockingQueueconQueue; public ConsumerQueue(BlockingQueue conQueue)       { this .conQueue =conQueue;       } @Override publicvoid run() { // TODO Auto-generated method stub for (int i=0;i<10;i++)              { try {                           System. out .println("消費者消費的蘋果編號為 :" +conQueue .take());                           Thread. sleep(3000);  //在這裡sleep是為了看的更加清楚些                     } catch (InterruptedException e) { // TODO: handle exception                           e.printStackTrace();                     }              }       }}結果如下:生產者生產的蘋果編號為 : 0生產者生產的蘋果編號為 : 1消費者消費的蘋果編號為 :0生產者生產的蘋果編號為 : 2生產者生產的蘋果編號為 : 3生產者生產的蘋果編號為 : 4生產者生產的蘋果編號為 : 5生產者生產的蘋果編號為 : 6生產者生產的蘋果編號為 : 7消費者消費的蘋果編號為 :1消費者消費的蘋果編號為 :2生產者生產的蘋果編號為 : 8消費者消費的蘋果編號為 :3生產者生產的蘋果編號為 : 9消費者消費的蘋果編號為 :4消費者消費的蘋果編號為 :5消費者消費的蘋果編號為 :6消費者消費的蘋果編號為 :7消費者消費的蘋果編號為 :8消費者消費的蘋果編號為 :9

生產者消費者模式的好處

它的確是一種實用的設計模式,常用於編寫多執行緒或併發程式碼。下面是它的一些優點:

  1.  它簡化的開發,你可以獨立地或併發的編寫消費者和生產者,它僅僅只需知道共享物件是誰
  2. 生產者不需要知道誰是消費者或者有多少消費者,對消費者來說也是一樣
  3.  生產者和消費者可以以不同的速度執行
  4.  分離的消費者和生產者在功能上能寫出更簡潔、可讀、易維護的程式碼

轉載自:http://blog.csdn.net/yujin753/article/details/45723175