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

          注:此示例來自MLDN講師李興華JAVA SE基礎(chǔ)教學(xué)部分

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

          示例一:

          package org.lx.multithreading;

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

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

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

          示例二

          package org.lx.multithreading;

          /**
           * 定義一個(gè)信息類
           * 
          @author Solitary
           
          */
          class Info {    
              
          private String name = "羅星" ;            //定義name屬性        
              private String content ="JAVA初學(xué)者";    //定義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)一設(shè)置信息
                  this.setName(name) ;
                  
          try {
                      
          /* 此時(shí)這個(gè)地方加不加延遲沒有任何關(guān)系,因?yàn)樵摲椒ㄒ呀?jīng)同步
                         在執(zhí)行到此方法(get())時(shí),此方法將會(huì)完整結(jié)束后才會(huì)執(zhí)行
                         到別的方法 ,所以一定會(huì)完整設(shè)置完信息之后才會(huì)輪到信息的讀取方法
          */
                      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實(shí)現(xiàn)多線程
              private Info info = null ;            //保存Info引用
              
              
          public Producer(Info info) {        //通過構(gòu)造方法傳遞引用
                  this.info = info ;
              }
              
              @Override
              
          public void run() {
                  
          boolean flag = false ;                //定義標(biāo)志位            
                  for(int i = 0; i < 50; i++) {        //生產(chǎn)50次信息
                      if(flag){    //如果標(biāo)志位為true 將設(shè)置 中文內(nèi)容
                          this.info.set("小星""JAVA初學(xué)者") ;
                          flag 
          = false ;    //改變標(biāo)志位,用于變換輸入內(nèi)容
                          
                      }
          else{    //如果標(biāo)志位為false 將設(shè)置英文內(nèi)容        
                          this.info.set("Solitary""Coder") ;
                          flag 
          = true ;    //改變標(biāo)志位,用于變換輸入內(nèi)容
                      }
                  }
              }    
              
          } ;

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

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

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


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

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

          實(shí)例三
          package org.lx.multithreading ;

          class Info        //定義信息類
          {
              
          private String name = "羅星" ;        //定義name屬性
              private String content = "JAVA初學(xué)者" ;        //定義content屬性
              private boolean flag = false ;    //設(shè)置標(biāo)志位


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

              
          public synchronized void get(){
                  
          if(flag){        ////方法每次執(zhí)行的時(shí)候都檢查一下標(biāo)志位狀態(tài),從而判斷時(shí)候進(jìn)行取出
                      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 ;    //改變標(biāo)志位,表示可以生成    
                  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實(shí)現(xiàn)多線程
          {
              
          public Info info = null ;        //保存Info引用
              public Producer(Info info){
                  
          this.info = info ;
              }

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

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

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

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

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

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

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

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


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

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

          評(píng)論

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

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


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


          網(wǎng)站導(dǎo)航:
           
          <2014年11月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 个旧市| 宜川县| 日照市| 交口县| 方山县| 油尖旺区| 临夏县| 南靖县| 体育| 金乡县| 安溪县| 曲麻莱县| 竹山县| 江城| 滦平县| 北京市| 嘉定区| 固始县| 上犹县| 内乡县| 科尔| 金门县| 桂阳县| 阳江市| 商洛市| 长丰县| 武山县| 尖扎县| 玉门市| 山阳县| 扶绥县| 潼南县| 恩平市| 肇源县| 屏东县| 安顺市| 龙里县| 临汾市| 乐昌市| 曲周县| 惠东县|