这一章节我们来讨论一下阻塞队列。我们下面将通过生产者消费者模式来介绍阻塞队列。
1.什么是阻塞队列?(摘自于并发编程网对http://tutorials.jenkov.com/java-concurrency/blocking-queues.html的翻译)
当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列,下图展示了如何通过阻塞队列来合作:
2.特性
(1)先进先出
(2)线程同步
3.下面结合生产者消费者模式来介绍阻塞队列的使用情况
- package com.ray.ch17;
- import java.util.concurrent.LinkedBlockingQueue;
- public class Test {
- public static void main(String[] args) {
- Basket basket = new Basket();
- Thread thread1 = new Thread(new Chief(basket));
- Thread thread2 = new Thread(new Customer(basket));
- Thread thread3 = new Thread(new Customer(basket));
- thread1.start();
- thread2.start();
- thread3.start();
- }
- }
- class Cake {
- private static int index = 0;
- private final int id = index++;
- public int getId() {
- return id;
- }
- public Cake() {
- System.out.println("生产了蛋糕,id:" + id);
- }
- }
- class Chief implements Runnable {
- private Basket basket = null;
- public Chief(Basket basket) {
- this.basket = basket;
- }
- public void makeCake() throws InterruptedException {
- for (int i = 0; i < 20; i++) {
- basket.put(new Cake());
- System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());
- Thread.sleep(20);
- }
- }
- @Override
- public void run() {
- try {
- makeCake();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- class Customer implements Runnable {
- private Basket basket = null;
- public Customer(Basket basket) {
- this.basket = basket;
- }
- public void buyCake() throws InterruptedException {
- System.out.println("消费了蛋糕,id:" + basket.take().getId());
- System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());
- }
- @Override
- public void run() {
- try {
- for (int i = 0; i < 10; i++) {
- buyCake();
- Thread.sleep(100);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- class Basket {
- private LinkedBlockingQueue<Cake> queue = new LinkedBlockingQueue<Cake>(10);
- public void put(Cake cake) throws InterruptedException {
- queue.put(cake);
- }
- public Cake take() throws InterruptedException {
- Cake cake = queue.take();
- return cake;
- }
- public int getQueueSize() {
- return queue.size();
- }
- }
输出:
生产了蛋糕,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,只有等到下一个消费者来消费才会继续执行,反之,如果消费的数量大于生产的数量也会出现这种情况;第二个是时间,我们需要按照实际的时间匹配关系来配对时间,如果生产大于消费,在出现生产一个消费一个,需要增加生产者,如果是消费大于生产,则根据实际需求来定。
下面的代码需要等待,就是因为消费的总数大于生产的总数而出现的问题:
- package com.ray.ch17;
- import java.util.concurrent.LinkedBlockingQueue;
- public class Test {
- public static void main(String[] args) {
- Basket basket = new Basket();
- Thread thread1 = new Thread(new Chief(basket));
- Thread thread2 = new Thread(new Customer(basket));
- Thread thread3 = new Thread(new Customer(basket));
- thread1.start();
- thread2.start();
- thread3.start();
- }
- }
- class Cake {
- private static int index = 0;
- private final int id = index++;
- public int getId() {
- return id;
- }
- public Cake() {
- System.out.println("生产了蛋糕,id:" + id);
- }
- }
- class Chief implements Runnable {
- private Basket basket = null;
- public Chief(Basket basket) {
- this.basket = basket;
- }
- public void makeCake() throws InterruptedException {
- for (int i = 0; i < 20; i++) {
- basket.put(new Cake());
- System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());
- Thread.sleep(200);
- }
- }
- @Override
- public void run() {
- try {
- makeCake();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- class Customer implements Runnable {
- private Basket basket = null;
- public Customer(Basket basket) {
- this.basket = basket;
- }
- public void buyCake() throws InterruptedException {
- System.out.println("消费了蛋糕,id:" + basket.take().getId());
- System.out.println("现在框里面的蛋糕数量:" + basket.getQueueSize());
- }
- @Override
- public void run() {
- try {
- for (int i = 0; i < 20; i++) {
- buyCake();
- Thread.sleep(100);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- class Basket {
- private LinkedBlockingQueue<Cake> queue = new LinkedBlockingQueue<Cake>(10);
- public void put(Cake cake) throws InterruptedException {
- queue.put(cake);
- }
- public Cake take() throws InterruptedException {
- Cake cake = queue.take();
- return cake;
- }
- public int getQueueSize() {
- return queue.size();
- }
- }
总结:这一章节我们通过生产者消费者模式来介绍了阻塞队列的特性。
这一章节就到这里,谢谢。
-----------------------------------