这一章节我们来讨论一下堵塞队列。我们以下将通过生产者消费者模式来介绍堵塞队列。
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(); } }
总结:这一章节我们通过生产者消费者模式来介绍了堵塞队列的特性。
这一章节就到这里,谢谢。
-----------------------------------