java 多线程并发系列之 生产者消费者模式的两种实现

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

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

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

我们看这样一个例子:

生产者:  往一个公共的盒子里面放苹果 
消费者:从公共的盒子里面取苹果
盒子:盒子的容量不能超过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.  分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码

猜你喜欢

转载自blog.csdn.net/jek123456/article/details/80505208