生產(chǎn)者與消費者(多線程經(jīng)典案例)

          注:此示例來自MLDN講師李興華JAVA SE基礎教學部分

          生產(chǎn)者和消費者是多線程中一個經(jīng)典的操作案例下面一起看下代碼:

          示例一:

          package org.lx.multithreading;

          /**
           * 定義一個信息類
           * 
          @author Solitary
           
          */
          class Info {    
              
          private String name = "羅星" ;            //定義name屬性        
              private String content ="JAVA初學者";    //定義content屬性
              
              
          //getter     setter
              public String getName() {
                  
          return name;
              }
              
          public void setName(String name) {
                  
          this.name = name;
              }
              
          public String getContent() {
                  
          return content;
              }
              
          public void setContent(String content) {
                  
          this.content = content;
              }
          } ;

          /**
           * 定義生產(chǎn)者
           * 
          @author Solitary
           
          */
          class Producer implements Runnable {    //通過接口Runnable實現(xiàn)多線程
              private Info info = null ;            //保存Info引用
              
              
          public Producer(Info info) {        //通過構(gòu)造方法傳遞引用
                  this.info = info ;
              }
              
              @Override
              
          public void run() {
                  
          boolean flag = false ;                //定義標志位            
                  for(int i = 0; i < 50; i++) {        //生產(chǎn)50次信息
                      if(flag){    //如果標志位為true 將設置 中文內(nèi)容
                          this.info.setName("小星") ;    //設置名字
                          try {
                              
          //為了更好的體現(xiàn)代碼運行效果在設置姓名和內(nèi)容之間加入延遲操作
                              Thread.sleep(300) ;
                          } 
          catch (InterruptedException e) {        //線程被打斷后會拋出此異常
                              e.printStackTrace();
                          }            
                          
          this.info.setContent("JAVA初學者") ;        //設置內(nèi)容
                          flag = false ;    //改變標志位,用于變換輸入內(nèi)容
                          
                      }
          else{    //如果標志位為false 將設置英文內(nèi)容        
                          this.info.setName("Solitary") ;    //設置名字
                          try {
                              
          //為了更好的體現(xiàn)代碼運行效果在設置姓名和內(nèi)容之間加入延遲操作
                              Thread.sleep(300) ;
                          } 
          catch (InterruptedException e) {        //線程被打斷后會拋出此異常
                              e.printStackTrace();
                          }            
                          
          this.info.setContent("Coder") ;        //設置內(nèi)容
                          flag = true ;    //改變標志位,用于變換輸入內(nèi)容
                      }
                  }
              }    
              
          } ;

          /**
           * 定義消費者
           * 
          @author Solitary
           
          */
          class Consumer implements Runnable {
              
          private Info info = null ;        //用于保存Info引用,其目的是為了讓消費者和生產(chǎn)者擁有同一個info
              
              
          public Consumer(Info info){
                  
          this.info = info ;
              }
              
          public void run() {
                  
          for(int i = 0; i < 50; i++) {        //消費和也從info中取50次消息
                      try {
                          Thread.sleep(
          300) ;
                      } 
          catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println(
          this.info.getName() + 
                              
          " --> " + this.info.getContent()) ;
                  }
              }
          } ;

          /**
           * 測試代碼
           * 
          @author Solitary\
           
          */
          public class MultiThreadingDemo01 {
              
          public static void main(String args[]){        
                  Info info 
          = new Info() ;    // 實例化Info對象
                  Producer pro = new Producer(info) ;        // 生產(chǎn)者
                  Consumer con = new Consumer(info) ;        // 消費者
                  new Thread(pro).start() ;        //啟動線程
                  new Thread(con).start() ;        //啟動線程        
              }
          }
          示例一(執(zhí)行效果):
          小星 --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          Solitary 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          Solitary 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> Coder
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          Solitary 
          --> JAVA初學者
          小星 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          Solitary 
          --> JAVA初學者
          Solitary 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          請注意運行結(jié)果,為什么我代碼里面明明成對設置的是:小星 --> JAVA初學者;  Solitary --> Coder而運行結(jié)果確實有不匹配的呢?
          分析:
          因為生產(chǎn)者和消費者的線程都已啟動,那么不能保證誰在前,或者誰在后,在生產(chǎn)者還在設置內(nèi)容的時候(比如:已經(jīng)設置好的Info的name=小星,Context=JAVA初學者,而此時生產(chǎn)者又設置了name = Solitary正打算設置Content = Coder),而消費者已經(jīng)取走了內(nèi)容,那么顯示的肯定就是Solitary --> JAVA初學者這樣的結(jié)果,因為兩個線程都在這執(zhí)行著,出現(xiàn)了不匹配的結(jié)果。可以將代碼修改為:

          示例二

          package org.lx.multithreading;

          /**
           * 定義一個信息類
           * 
          @author Solitary
           
          */
          class Info {    
              
          private String name = "羅星" ;            //定義name屬性        
              private String content ="JAVA初學者";    //定義content屬性
              
              
          //getter     setter
              public String getName() {
                  
          return name;
              }
              
          public void setName(String name) {
                  
          this.name = name;
              }
              
          public String getContent() {
                  
          return content;
              }
              
          public void setContent(String content) {
                  
          this.content = content ;
              }
              
              
          public synchronized void set(String name, String content){        //由此方法統(tǒng)一設置信息
                  this.setName(name) ;
                  
          try {
                      
          /* 此時這個地方加不加延遲沒有任何關(guān)系,因為該方法已經(jīng)同步
                         在執(zhí)行到此方法(get())時,此方法將會完整結(jié)束后才會執(zhí)行
                         到別的方法 ,所以一定會完整設置完信息之后才會輪到信息的讀取方法
          */
                      Thread.sleep(
          300) ;            
                  } 
          catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  
          this.setContent(content) ;
              }
              
              
          public synchronized void get(){
                  
          try {
                      Thread.sleep(
          300) ;
                  } 
          catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(
          this.getName() + 
                          
          " --> " + this.getContent()) ;
              }
          } ;

          /**
           * 定義生產(chǎn)者
           * 
          @author Solitary
           
          */
          class Producer implements Runnable {    //通過接口Runnable實現(xiàn)多線程
              private Info info = null ;            //保存Info引用
              
              
          public Producer(Info info) {        //通過構(gòu)造方法傳遞引用
                  this.info = info ;
              }
              
              @Override
              
          public void run() {
                  
          boolean flag = false ;                //定義標志位            
                  for(int i = 0; i < 50; i++) {        //生產(chǎn)50次信息
                      if(flag){    //如果標志位為true 將設置 中文內(nèi)容
                          this.info.set("小星""JAVA初學者") ;
                          flag 
          = false ;    //改變標志位,用于變換輸入內(nèi)容
                          
                      }
          else{    //如果標志位為false 將設置英文內(nèi)容        
                          this.info.set("Solitary""Coder") ;
                          flag 
          = true ;    //改變標志位,用于變換輸入內(nèi)容
                      }
                  }
              }    
              
          } ;

          /**
           * 定義消費者
           * 
          @author Solitary
           
          */
          class Consumer implements Runnable {
              
          private Info info = null ;        //用于保存Info引用,其目的是為了讓消費者和生產(chǎn)者擁有同一個info
              
              
          public Consumer(Info info){
                  
          this.info = info ;
              }
              
          public void run() {
                  
          for(int i = 0; i < 50; i++) {        //消費和也從info中取50次消息
                      this.info.get() ;
                  }
              }
          } ;

          /**
           * 測試代碼
           * 
          @author Solitary\
           
          */
          public class MultiThreadingDemo01 {
              
          public static void main(String args[]){        
                  Info info 
          = new Info() ;    // 實例化Info對象
                  Producer pro = new Producer(info) ;        // 生產(chǎn)者
                  Consumer con = new Consumer(info) ;        // 消費者
                  new Thread(pro).start() ;        //啟動線程
                  new Thread(con).start() ;        //啟動線程        
              }
          }
          示例二(運行效果)
          Solitary --> Coder
          Solitary 
          --> Coder
          Solitary 
          --> Coder
          Solitary 
          --> Coder
          Solitary 
          --> Coder
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          Solitary 
          --> Coder
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          小星 
          --> JAVA初學者
          觀察運行結(jié)果發(fā)現(xiàn),不匹配的現(xiàn)象已經(jīng)解決了,因為設置name 與 content 的步驟都在一個同步方法中,所以不會導致設置不完整的情況,但是并沒有達到我們想要的結(jié)果, 觀察出現(xiàn)了連續(xù)出現(xiàn)重復的內(nèi)容,這是為什么呢? 分析:這兩個方法在不同的線程中,當一條線程中的方法設置完內(nèi)容之后,另一個線程取出內(nèi)容顯示,取完之后當了土財主不讓,繼續(xù)取,生產(chǎn)者無法更新里邊的內(nèi)容,那顯示出來的內(nèi)容肯定就是重復的,因為多線程中,不能保證這個線程什么時候執(zhí)行。怎樣解決呢?

          1.中間那塊矩形代表信息載體Info實例,載體中沒有產(chǎn)品的時候上面顯示為綠燈,那么這時生產(chǎn)者能放入產(chǎn)品。而消費者不能取出產(chǎn)品。


          2.當生產(chǎn)者放入產(chǎn)品之后燈變成紅色,這時候生產(chǎn)者將不能放入產(chǎn)品,而輪到消費者取出產(chǎn)品,那么去完之后再次改變燈為綠色。這樣一直反復執(zhí)行下去。

          那么載體上的那盞燈屬于一個標志位,我們可以在代碼中用boolean表示,那么這盞燈是屬于信息載體Info的標志,因為設置/取出這兩個方法都在Info中定義,生產(chǎn)者與消費者只是負責調(diào)用Info之中的方法,就讓Info中的方法判斷一下自身的標志位的狀態(tài)判斷是否生產(chǎn)或者取出。

          實例三
          package org.lx.multithreading ;

          class Info        //定義信息類
          {
              
          private String name = "羅星" ;        //定義name屬性
              private String content = "JAVA初學者" ;        //定義content屬性
              private boolean flag = false ;    //設置標志位


              
          public synchronized void set(String name, String content){
                  
          if(!flag){        //方法每次執(zhí)行的時候都檢查一下標志位狀態(tài),從而判斷時候進行生產(chǎn)
                      try{
                          
          super.wait() ;
                      }
          catch(InterruptedException e){
                          e.printStackTrace() ;
                      }
                  }
                  
          this.setName(name) ;        //設置名稱
                  try{
                      Thread.sleep(
          300) ;
                  }
          catch(InterruptedException e){
                      e.printStackTrace() ;
                  }
                  
          this.setContent(content) ;    // 設置內(nèi)容
                  flag = false ;        //改變標志位,表示可以取走
                  super.notify() ;    //喚醒線程
              }

              
          public synchronized void get(){
                  
          if(flag){        ////方法每次執(zhí)行的時候都檢查一下標志位狀態(tài),從而判斷時候進行取出
                      try{
                          
          super.wait() ;
                      }
          catch(InterruptedException e){
                          e.printStackTrace() ;
                      }
                  }
                  
          try{
                      Thread.sleep(
          300) ;
                  }
          catch(InterruptedException e){
                      e.printStackTrace() ;
                  }
                  System.out.println(
          this.getName() +
                          
          " --> " + this.getContent()) ;
                  flag 
          = true ;    //改變標志位,表示可以生成    
                  super.notify() ;
              }

              
          public void setName(String name){
                  
          this.name = name ;
              }

              
          public String getName(){
                  
          return this.name ;
              }

              
          public void setContent(String content){
                  
          this.content = content ;
              }

              
          public String getContent(){
                  
          return this.content ;
              }
          }

          class Producer implements Runnable        //通過Runnable實現(xiàn)多線程
          {
              
          public Info info = null ;        //保存Info引用
              public Producer(Info info){
                  
          this.info = info ;
              }

              
          public void run(){
                  
          boolean flag = false ;    //定義標記位
                  for(int i = 0; i < 50; i++){
                      
          if(flag){
                          
          this.info.set("羅星""JAVA初學者") ;
                          flag 
          = false ;
                      }
          else{
                          
          this.info.set("Solitary""Coder") ;
                          flag 
          = true ;
                      }
                  }
              }
          } ;

          class Consumer implements Runnable{        //消費者類
              private Info info = null ;        
              
          public Consumer(Info info){
                  
          this.info = info ;
              }

              
          public void run(){
                  
          for(int i = 0; i < 50; i++){
                      
          this.info.get() ;
                  }
              }
          } ;

          //測試代碼
          public class MultiThreading
          {
              
          public static void main(String[] args){
                  Info info 
          = new Info() ;    //實例化Info對象
                  Producer pro = new Producer(info) ;    //生產(chǎn)者
                  Consumer con = new Consumer(info) ;    //消費者
                  new Thread(pro).start() ;
                  
          new Thread(con).start() ;
              }
          }
          在此利用了Object類對線程的支持,Object中定義了方法:
          public final void wait() throws InterruptedException
          在其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。
          換句話說,此方法的行為就好像它僅執(zhí)行 wait(0) 調(diào)用一樣。

          當前線程必須擁有此對象監(jiān)視器。該線程發(fā)布對此監(jiān)視器的所有權(quán)并等待,直到其他線程通過調(diào)用 notify 方法,或 notifyAll 方法通知在此對象的監(jiān)視器上等待的線程醒來。然后該線程將等到重新獲得對監(jiān)視器的所有權(quán)后才能繼續(xù)執(zhí)行。 

          public final void notify()
          喚醒在此對象監(jiān)視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,并在對實現(xiàn)做出決定時發(fā)生。線程通過調(diào)用其中一個 wait 方法,在對象的監(jiān)視器上等待。

          直到當前線程放棄此對象上的鎖定,才能繼續(xù)執(zhí)行被喚醒的線程。被喚醒的線程將以常規(guī)方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權(quán)或劣勢。  

          當生產(chǎn)者線程調(diào)用此方法時,首先檢查一下標志位,判斷是否等待,此時消費者也在調(diào)用相應方法判斷是否取出,如果可以取出,那么取出后改變標志位的狀態(tài),然后喚醒該對象中等待的線程,這時狀態(tài)改變生產(chǎn)者既進行生產(chǎn)操作。


          序言:
          小弟是一個JAVA新手,這也是第一次寫博客,此文純屬于學習筆記,以便日后復習,如果前輩們認為描述有錯誤的地方,或者覺得不清晰感覺思路混亂的地方,還請前輩們多多賜教,晚生感激不勝! 謝謝。

          posted on 2011-10-29 18:26 Solitary 閱讀(2314) 評論(1)  編輯  收藏

          評論

          # re: 生產(chǎn)者與消費者(多線程經(jīng)典案例) 2014-11-11 21:53 李興華

          親,你這明顯是抄襲我的嘛,我是魔樂科技的李興華;
          希望你能改過自新  回復  更多評論   


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
           
          <2011年10月>
          2526272829301
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          導航

          統(tǒng)計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 蒙城县| 余姚市| 淳安县| 息烽县| 青河县| 天门市| 锦州市| 胶南市| 石林| 鲁甸县| 莱州市| 石泉县| 佛教| 洮南市| 钟祥市| 玛沁县| 嘉荫县| 文登市| 普宁市| 北海市| 黎平县| 鲁甸县| 河池市| 荔浦县| 抚州市| 北票市| 二手房| 比如县| 临湘市| 涟源市| 泰顺县| 南康市| 调兵山市| 循化| 乾安县| 岳阳市| 棋牌| 台州市| 普定县| 循化| 清新县|