(三)实际项目中数据结构—队列的应用

1.队列:

定义: 一种数据结构,有非阻塞对列和阻塞对列
特点:先进先出
两种典型操作: 队尾添加、队头删除
几种对列:
非阻塞队列:当对列满或空时进行插入或者读取删除操作,抛出异常或者返回false,不对当前线程阻塞,没有同步或者唤醒策略。
阻塞队列: 当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。
有界队列 一般来说队列的长度都预先定义好大小,这样的队列就是有界对列。
无界 不具有预定义容量的队列

2.阻塞队列使用:

多线程协调,合作

线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素。

3.java中阻塞队列种类:

LinkedBlockingQueue:由链表结构组成的有界阻塞队列,此队列按 FIFO(先进先出)排序元素。此队列的默认和最大长度为Integer.MAX_VALUE。
LinkedTransferQueue由链表结构组成的无界阻塞队列。
LinkedBlockingDeque由链表结构组成的双向阻塞队列。
ArrayBlockingQueue: 由数组结构组成的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。构造时需要指定容量,并可以选择是否需要公平性。如果公平参数被设置true,等待时间最长的线程会优先得到处理,会使你在性能上付出代价。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限,put时是不会受阻的,对列为空,take时就会阻塞。进入该队列中的元素要具有比较能力。默认情况下元素采取自然顺序排列,也可以通过比较器comparator来指定元素的排序规则。元素按照升序排列。
DelayQueue:是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。
DelayQueue运用在以下应用场景:
缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。
SynchronousQueue:不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue 和 ArrayBlockingQueue。

4.阻塞队列控制线程操作实例:

多线程操作共同的队列时不需要额外的同步,队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。 不需要再单独考虑同步和线程间通信的问题。
  1. public class Test {  
  2.     private int queueSize = 10;  
  3.     private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize);  
  4.     public static void main(String[] args) {  
  5.         Test test = new Test();  
  6.         Producer producer = test.new Producer();  
  7.         Consumer consumer = test.new Consumer();  
  8.         producer.start();  
  9.         consumer.start();  
  10.     }  
  11.     class Consumer extends Thread{  
  12.         @Override  
  13.         public void run() {  
  14.             consume();  
  15.         }  
  16.         private void consume() {  
  17.             while(true){  
  18.                 try {  
  19.                     queue.take();  
  20.                     System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");  
  21.                 } catch (InterruptedException e) {  
  22.                     e.printStackTrace();  
  23.                 }  
  24.             }  
  25.         }  
  26.     }  
  27.     class Producer extends Thread{  
  28.         @Override  
  29.         public void run() {  
  30.             produce();  
  31.         }  
  32.         private void produce() {  
  33.             while(true){  
  34.                 try {  
  35.                     queue.put(1);  
  36.                     System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));  
  37.                 } catch (InterruptedException e) {  
  38.                     e.printStackTrace();  
  39.                 }  
  40.             }  
  41.         }  
  42.     }  
  43. }  


  1. <pre code_snippet_id="1979091" snippet_file_name="blog_20161111_1_7281024" name="code" class="java"><pre code_snippet_id="1979091" snippet_file_name="blog_20161111_1_7281024"></pre>  
  2. <pre></pre>  
  3. <pre></pre>  
  4. <pre></pre>  
  5. <pre></pre>  
  6. <pre></pre>  
  7. <pre></pre>  
  8. <pre></pre>  
  9. <pre></pre>  
  10. </pre>  
在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。
阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值