生产者消费者模式是并发、多线程编程中经典的设计模式 ,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据。




真实世界中的生产者消费者模式

生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。这里桌子就是一个共享的对象。


我们看这样一个例子:




生产者:  往一个公共的盒子里面放苹果 


消费者:从公共的盒子里面取苹果


盒子:盒子的容量不能超过5




下面我们用两者方法分别实现这样一个场景。




方法一:   wait()  和   notify()   通信方法实现




看盒子代码


        

public 
    
  class  
  PublicBox { 
 
 
            
  private 
    
  int  
  apple  
  = 0;  
      
 
 
            
  public 
    
  synchronized  
  void 
   increace() { 
 
 
                
  while 
  ( 
  apple  
  ==5) { 
 
 
                    
  try 
  { 
 
 
                       wait(); 
 
 
                   }  
  catch 
  (InterruptedException e) { 
 
 
                       e.printStackTrace(); 
 
 
                   } 
 
 
                  
 
 
               } 
 
 
                
  apple 
  ++; 
 
 
               
   System.  
  out 
  .println( 
  "生成苹果成功!"  
  );
 
 
               notify(); 
 
 
           }  
        
 
 
            
  public 
    
  synchronized  
  void 
   decreace() { 
 
 
                
  while 
  ( 
  apple  
  ==0) { 
 
 
                    
  try 
  { 
 
 
                       wait(); 
 
 
                   }  
  catch 
  (InterruptedException e) { 
 
 
                       e.printStackTrace(); 
 
 
                   } 
 
 
               } 
 
 
                
  apple 
  --; 
 
 
               
  System. 
  out 
  .println(  
  "消费苹果成功!"  
  );
 
 
               notify(); 
 
 
           } 
 
 
          
 
 
            
  public 
    
  static  
  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();
 
 
                     
 
 
                     
 
 
              }
 
 
       }

生产者代码(定义十次):




public 
    
  class  
  Producer  
  implements 
   Runnable { 
 
 
     
  private 
   PublicBox  
  box 
  ; 
 
 
 
 
 
     
  public 
   Producer(PublicBox box) { 
 
 
         
  this 
  . 
  box  
  = box; 
 
 
    } 
 
 
 
 
 
     
  @Override 
   
 
 
     
  public 
    
  void 
  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(); 
 
 
       }
 
 
        
 
 
    } 
 
 
}


消费者代码(同样十次):





public   class  Consumer  implements  Runnable { 



     private  PublicBox  box ; 



 



public 
   Consumer(PublicBox box) { 
 
 
         
  this 
  . 
  box  
  = box; 
 
 
    } 
 
 
 
 
 
     
  @Override 
   
 
 
     
  public 
    
  void 
  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:0



Con: 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:8



Con: i 3



消费苹果成功!



生成苹果成功!



pro  i:9



Con: 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;
 
 

 
 
public 
    
  class  
  PublicBoxQueue {
 
 

 
 
       
 
 
         
  public 
    
  static  
  void 
   main(String []args)
 
 
       {
 
 
               BlockingQueue publicBoxQueue= 
  new 
   LinkedBlockingQueue(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;
 
 

 
 
public 
    
  class  
  ProducerQueue  
  implements 
   Runnable {
 
 

 
 
         
  private 
    
  final  
  BlockingQueue  
  proQueue 
  ;
 
 
       
 
 
         
  public 
  ProducerQueue(BlockingQueue proQueue)
 
 
       {
 
 
                
  this 
  . 
  proQueue  
  =proQueue;
 
 
       }
 
 

 
 
         
  @Override
 
 
         
  public 
    
  void  
  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;
 
 

 
 
public 
    
  class  
  ConsumerQueue  
  implements 
   Runnable {
 
 

 
 
         
  private 
    
  final  
  BlockingQueue  
  conQueue 
  ;
 
 
       
 
 
         
  public 
  ConsumerQueue(BlockingQueue conQueue)
 
 
       {
 
 
                
  this 
  . 
  conQueue  
  =conQueue;
 
 
       }
 
 

 
 
         
  @Override
 
 
         
  public 
    
  void  
  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.  分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码