生产者消费者模式和阻塞队列

这一章节我们来讨论一下阻塞队列。我们下面将通过生产者消费者模式来介绍阻塞队列。

1.什么是阻塞队列?(摘自于并发编程网对http://tutorials.jenkov.com/java-concurrency/blocking-queues.html的翻译)

当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列,下图展示了如何通过阻塞队列来合作:



2.特性

(1)先进先出

(2)线程同步


3.下面结合生产者消费者模式来介绍阻塞队列的使用情况

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.ray.ch17;  
  2.   
  3. import java.util.concurrent.LinkedBlockingQueue;  
  4.   
  5. public class Test {  
  6.   
  7.     public static void main(String[] args) {  
  8.         Basket basket = new Basket();  
  9.         Thread thread1 = new Thread(new Chief(basket));  
  10.         Thread thread2 = new Thread(new Customer(basket));  
  11.         Thread thread3 = new Thread(new Customer(basket));  
  12.         thread1.start();  
  13.         thread2.start();  
  14.         thread3.start();  
  15.     }  
  16. }  
  17.   
  18. class Cake {  
  19.     private static int index = 0;  
  20.     private final int id = index++;  
  21.   
  22.     public int getId() {  
  23.         return id;  
  24.     }  
  25.   
  26.     public Cake() {  
  27.         System.out.println("生产了蛋糕,id:" + id);  
  28.     }  
  29. }  
  30.   
  31. class Chief implements Runnable {  
  32.     private Basket basket = null;  
  33.   
  34.     public Chief(Basket basket) {  
  35.         this.basket = basket;  
  36.     }  
  37.   
  38.     public void makeCake() throws InterruptedException {  
  39.         for (int i = 0; i < 20; i++) {  
  40.             basket.put(new Cake());  
  41.             System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());  
  42.             Thread.sleep(20);  
  43.         }  
  44.     }  
  45.   
  46.     @Override  
  47.     public void run() {  
  48.         try {  
  49.             makeCake();  
  50.         } catch (InterruptedException e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.     }  
  54.   
  55. }  
  56.   
  57. class Customer implements Runnable {  
  58.     private Basket basket = null;  
  59.   
  60.     public Customer(Basket basket) {  
  61.         this.basket = basket;  
  62.     }  
  63.   
  64.     public void buyCake() throws InterruptedException {  
  65.         System.out.println("消费了蛋糕,id:" + basket.take().getId());  
  66.         System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());  
  67.     }  
  68.   
  69.     @Override  
  70.     public void run() {  
  71.         try {  
  72.             for (int i = 0; i < 10; i++) {  
  73.                 buyCake();  
  74.                 Thread.sleep(100);  
  75.             }  
  76.         } catch (InterruptedException e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80. }  
  81.   
  82. class Basket {  
  83.     private LinkedBlockingQueue<Cake> queue = new LinkedBlockingQueue<Cake>(10);  
  84.   
  85.     public void put(Cake cake) throws InterruptedException {  
  86.         queue.put(cake);  
  87.     }  
  88.   
  89.     public Cake take() throws InterruptedException {  
  90.         Cake cake = queue.take();  
  91.         return cake;  
  92.     }  
  93.   
  94.     public int getQueueSize() {  
  95.         return queue.size();  
  96.     }  
  97. }  

输出:

生产了蛋糕,id:0
消费了蛋糕,id:0
现在框里面的蛋糕数量:0
现在框里面的蛋糕数量:0
生产了蛋糕,id:1
现在框里面的蛋糕数量:1
消费了蛋糕,id:1
现在框里面的蛋糕数量:0
生产了蛋糕,id:2
现在框里面的蛋糕数量:1
生产了蛋糕,id:3
现在框里面的蛋糕数量:2
生产了蛋糕,id:4
现在框里面的蛋糕数量:3
消费了蛋糕,id:2
现在框里面的蛋糕数量:2
生产了蛋糕,id:5
现在框里面的蛋糕数量:3
消费了蛋糕,id:3
现在框里面的蛋糕数量:2
生产了蛋糕,id:6
现在框里面的蛋糕数量:3
生产了蛋糕,id:7
现在框里面的蛋糕数量:4
生产了蛋糕,id:8
现在框里面的蛋糕数量:5
生产了蛋糕,id:9
现在框里面的蛋糕数量:6
消费了蛋糕,id:4
现在框里面的蛋糕数量:5
消费了蛋糕,id:5
现在框里面的蛋糕数量:4
消费了蛋糕,id:6
现在框里面的蛋糕数量:3
消费了蛋糕,id:7
现在框里面的蛋糕数量:2
消费了蛋糕,id:8
现在框里面的蛋糕数量:1
消费了蛋糕,id:9
现在框里面的蛋糕数量:0


解释:

(1)由于是生产者消费者模式,因此上面必须有生产者、消费者、篮子和消费的物品(蛋糕)这四个类,还有一个Test测试类

(2)在Cake类里面,我们使用static的index来记录每一个Cake的id

(3)在Basket类里面我们除了put和take方法,还放入一个getSize的方法来得到篮子里面现在有多少个Cake

(4)生产者和消费者其实只有一个方法就是生产make或者buy,这里面需要注意的是两个地方,第一个是数量,由于我们上面使用的是阻塞队列,因此当生产的总数量大于消费的总数量,程序就会一直在wait,只有等到下一个消费者来消费才会继续执行,反之,如果消费的数量大于生产的数量也会出现这种情况;第二个是时间,我们需要按照实际的时间匹配关系来配对时间,如果生产大于消费,在出现生产一个消费一个,需要增加生产者,如果是消费大于生产,则根据实际需求来定。


下面的代码需要等待,就是因为消费的总数大于生产的总数而出现的问题:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.ray.ch17;  
  2.   
  3. import java.util.concurrent.LinkedBlockingQueue;  
  4.   
  5. public class Test {  
  6.   
  7.     public static void main(String[] args) {  
  8.         Basket basket = new Basket();  
  9.         Thread thread1 = new Thread(new Chief(basket));  
  10.         Thread thread2 = new Thread(new Customer(basket));  
  11.         Thread thread3 = new Thread(new Customer(basket));  
  12.         thread1.start();  
  13.         thread2.start();  
  14.         thread3.start();  
  15.     }  
  16. }  
  17.   
  18. class Cake {  
  19.     private static int index = 0;  
  20.     private final int id = index++;  
  21.   
  22.     public int getId() {  
  23.         return id;  
  24.     }  
  25.   
  26.     public Cake() {  
  27.         System.out.println("生产了蛋糕,id:" + id);  
  28.     }  
  29. }  
  30.   
  31. class Chief implements Runnable {  
  32.     private Basket basket = null;  
  33.   
  34.     public Chief(Basket basket) {  
  35.         this.basket = basket;  
  36.     }  
  37.   
  38.     public void makeCake() throws InterruptedException {  
  39.         for (int i = 0; i < 20; i++) {  
  40.             basket.put(new Cake());  
  41.             System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());  
  42.             Thread.sleep(200);  
  43.         }  
  44.     }  
  45.   
  46.     @Override  
  47.     public void run() {  
  48.         try {  
  49.             makeCake();  
  50.         } catch (InterruptedException e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.     }  
  54.   
  55. }  
  56.   
  57. class Customer implements Runnable {  
  58.     private Basket basket = null;  
  59.   
  60.     public Customer(Basket basket) {  
  61.         this.basket = basket;  
  62.     }  
  63.   
  64.     public void buyCake() throws InterruptedException {  
  65.         System.out.println("消费了蛋糕,id:" + basket.take().getId());  
  66.         System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());  
  67.     }  
  68.   
  69.     @Override  
  70.     public void run() {  
  71.         try {  
  72.             for (int i = 0; i < 20; i++) {  
  73.                 buyCake();  
  74.                 Thread.sleep(100);  
  75.             }  
  76.         } catch (InterruptedException e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80. }  
  81.   
  82. class Basket {  
  83.     private LinkedBlockingQueue<Cake> queue = new LinkedBlockingQueue<Cake>(10);  
  84.   
  85.     public void put(Cake cake) throws InterruptedException {  
  86.         queue.put(cake);  
  87.     }  
  88.   
  89.     public Cake take() throws InterruptedException {  
  90.         Cake cake = queue.take();  
  91.         return cake;  
  92.     }  
  93.   
  94.     public int getQueueSize() {  
  95.         return queue.size();  
  96.     }  
  97. }  





总结:这一章节我们通过生产者消费者模式来介绍了阻塞队列的特性。


这一章节就到这里,谢谢。

-----------------------------------

目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者消费者模式是一种常见的并发编程模型,队列可以作为其实现方式之一。Java中可以使用BlockingQueue来实现队列的功能,具体步骤如下: 1. 定义一个BlockingQueue对象作为生产者和消费者之间的缓冲区。 ``` BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10); ``` 2. 定义一个生产者类,该类会不断地向队列中添加数据,直到队列满为止。 ``` class Producer implements Runnable { private BlockingQueue<Integer> queue; public Producer(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { try { while (true) { int i = new Random().nextInt(); queue.put(i); System.out.println("生产者生产数据:" + i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 3. 定义一个消费者类,该类会不断地从队列中取出数据,直到队列为空为止。 ``` class Consumer implements Runnable { private BlockingQueue<Integer> queue; public Consumer(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { try { while (true) { int i = queue.take(); System.out.println("消费者消费数据:" + i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 4. 创建一个线程池,并启动生产者和消费者线程。 ``` ExecutorService executor = Executors.newFixedThreadPool(2); executor.submit(new Producer(queue)); executor.submit(new Consumer(queue)); ``` 这样就完成了生产者消费者模式的实现。生产者会不断地向队列中添加数据,消费者会不断地从队列中取出数据,当队列满时,生产者会阻塞等待,当队列为空时,消费者会阻塞等待。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值