Java线程(篇外篇):阻塞队列BlockingQueue

       好久没有写文章了,这段时间事情比较杂,工作也比较杂乱,上周日刚搬完家,从自建房搬到了楼房,提升了一层生活品质,哈哈!不过昨天晚上在公交车上钱包被偷了,前段时间还丢个自行车,不得不感叹,京城扒手真多,还无人处理。言归正传,这一段时间我的工作主要是改进公司的调度器,调度器调度线程池执行任务,生产者生产任务,消费者消费任务,那么这时就需要一个任务队列,生产者向队列里插入任务,消费者从队列里提取任务执行,调度器里是通过BlockingQueue实现的队列,随后小查一下,下面看看BlockingQueue的原理及其方法。

       BlockingQueue最终会有四种状况,抛出异常、返回特殊值、阻塞、超时,下表总结了这些方法:

  抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
检查 element() peek() 不可用 不可用

       BlockingQueue是个接口,有如下实现类:

       1. ArrayBlockQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。创建其对象必须明确大小,像数组一样。

       2. LinkedBlockQueue:一个可改变大小的阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。创建其对象如果没有明确大小,默认值是Integer.MAX_VALUE。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。 

       3. PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。

       4. SynchronousQueue:同步队列。同步队列没有任何容量,每个插入必须等待另一个线程移除,反之亦然。

       下面使用ArrayBlockQueue来实现之前实现过的生产者消/费者模式,代码如下:

View Code
 1 /** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */
 2 public class BigPlate {
 3 
 4     /** 装鸡蛋的盘子,大小为5 */
 5     private BlockingQueue<Object> eggs = new ArrayBlockingQueue<Object>(5);
 6     
 7     /** 放鸡蛋 */
 8     public void putEgg(Object egg) {
 9         try {
10             eggs.put(egg);// 向盘子末尾放一个鸡蛋,如果盘子满了,当前线程阻塞
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14         System.out.println("放入鸡蛋");
15     }
16     
17     /** 取鸡蛋 */
18     public Object getEgg() {
19         Object egg = null;
20         try {
21             egg = eggs.take();// 从盘子开始取一个鸡蛋,如果盘子空了,当前线程阻塞
22         } catch (InterruptedException e) {
23             e.printStackTrace();
24         }
25         System.out.println("拿到鸡蛋");
26         return egg;
27     }
28     
29     /** 放鸡蛋线程 */
30     static class AddThread extends Thread {
31         private BigPlate plate;
32         private Object egg = new Object();
33 
34         public AddThread(BigPlate plate) {
35             this.plate = plate;
36         }
37 
38         public void run() {
39             plate.putEgg(egg);
40         }
41     }
42 
43     /** 取鸡蛋线程 */
44     static class GetThread extends Thread {
45         private BigPlate plate;
46 
47         public GetThread(BigPlate plate) {
48             this.plate = plate;
49         }
50 
51         public void run() {
52             plate.getEgg();
53         }
54     }
55     
56     public static void main(String[] args) {
57         BigPlate plate = new BigPlate();
58         // 先启动10个放鸡蛋线程
59         for(int i = 0; i < 10; i++) {
60             new Thread(new AddThread(plate)).start();
61         }
62         // 再启动10个取鸡蛋线程
63         for(int i = 0; i < 10; i++) {
64             new Thread(new GetThread(plate)).start();
65         }
66     }
67 }
   执行结果:
 1 放入鸡蛋
 2 放入鸡蛋
 3 放入鸡蛋
 4 放入鸡蛋
 5 放入鸡蛋
 6 拿到鸡蛋
 7 放入鸡蛋
 8 拿到鸡蛋
 9 拿到鸡蛋
10 拿到鸡蛋
11 放入鸡蛋
12 放入鸡蛋
13 放入鸡蛋
14 拿到鸡蛋
15 放入鸡蛋
16 拿到鸡蛋
17 拿到鸡蛋
18 拿到鸡蛋
19 拿到鸡蛋
20 拿到鸡蛋

       从结果看,启动10个放鸡蛋线程和10个取鸡蛋线程,钱5个放入鸡蛋的线程成功执行,到第6个,发现盘子满了,阻塞住,这时切换到取鸡蛋线程执行,成功实现了生产者/消费者模式。java.util.concurrent包是个强大的包!

        本文来自:高爽|JavaAnd Flex Corder,原文地址:http://blog.csdn.net/ghsau/article/details/8108292

posted @ 2012-10-25 12:13  高爽|Coder  阅读(375)  评论(0编辑  收藏  举报