Queue接口继承了Collection接口,其内部定义了六个方法,分为三大类,分别是新增元素、删除元素、检索元素。每一个大类都提供了两个方法,这两个方法的作用可以用下面的表格来描述:

(1).add()和offer():这两个方法是向队列添加元素,不同之处在于如果队列已经添加满了还继续添加add()方法就会抛出异常,而offer()会返回false值(如果队列没有满,则offer()添加成功后会返回true)。

(2).remove()和poll():这两个方法都是返回队列顶元素并且从队列中删除。不同的是,如果在删除时队列里没有元素了那么remove则会抛异常,而poll则会返回null。

(3).element()和peek():这两个方法都是取队列顶部元素但是不删除,和remove()、poll()有所区别。当队列没有元素时element()会抛异常,而peek()会返回null。

一、双向队列Deque

双向队列可以双向操作,比如添加元素可以从两头添加、删除和获取都可以从收尾删除获取,这些操作都对应于相应的last和first方法。Deque也是一个双向队列接口,其下的实现由ArrayDeque(它的大小没有限制,也可以手动限制)。

下面我们看双向队列的实现原理。我们以ArrayDeque类分析。

其实如果在创建ArrayDeque对象时不指定其初始大小它默认的就是16,如果在使用的时候发现不够就会拓展。双向队列的数据存储实际上时用数组来存储的,在ArrayDeque中有一个全局的E[] elements,数组,之后所有的增删改查都与此数据息息相关,此外还有两个比较重要的变量就是head和tail变量,分别表示队列首部和尾部。当我们从队列的头部取值时head值会自动+1,且会将elements[head]元素置为null(如果是remove方法或poll方法),如果是从队列的尾部取值,则tail的值会自动-1.其实现原理如下:

    public E pollFirst() {int h = head;E result = elements[h]; // Element is null if deque emptyif (result == null)return null;elements[h] = null;     // Must null out slothead = (h + 1) & (elements.length - 1);return result;}public E pollLast() {int t = (tail - 1) & (elements.length - 1);E result = elements[t];if (result == null)return null;elements[t] = null;tail = t;return result;}

虽然上面给出的只是pollFirst()和poolLast()方法,但是实际上poll()也是调用的pollFIrst()方法。

二、阻塞队列BlockingQueue

先来总结下BlockingQueue的特性:

1、阻塞队列在我们处理生产者-消费者模式的时候是很有用的,尤其是如果有大量网络请求的应用场景。当队列里的元素为空时,他就等处于等待状态,等待队列里存储进新的任务或元素。
  2、BlockingQueue的四种类型的方法,每一种类型都有不一样的操作,有时候可以根据不同的场景去使用。这四种不同的类型分别是:抛出异常、返回一个特定的值(null、false、0等)、无限期的阻塞当前线程直到操作成功、在放弃任务之前等待的时间。
  3、BlockingQueue不接受null元素。

4、BlockingQueue必须有一个容量限制。
  5、BlockingQueue是线程安全的,其所有的方法都有内部的Lock保证安全。
  6、BlockingQueue没有任何类似“close”、“shutdown”等操作去指示不能添加更多的元素。

如下便是四种不同类型的方法,和普通的Queue就多了个阻塞机制:

  Throws exception Special value Blocks Times out
Insert add(e) offer(e) put(e) offer(e, time, unit)
Remove remove() poll() take() poll(time, unit)
Examine element() peek() not applicable not applicable

方法的用于上述图标已经很明了,下面我们看一个典型的生产者和消费者的例子:

(1)生产者

 /*** 生产者* * @author Administrator* */public static class Producer implements Runnable {private final BlockingQueue queue;Producer(BlockingQueue q) {queue = q;}public void run() {try {while (true) {// 生产对象/任务,queue.put(produce());}} catch (InterruptedException ex) {}}Object produce() {cout++;return "第" + cout + "个任务";}}

(2)消费者

 /*** 消费者* * @author Administrator* */public static class Consumer implements Runnable {private final BlockingQueue queue;Consumer(BlockingQueue q) {queue = q;}public void run() {try {while (true) {// 从队列取出元素/任务,如果没有任务就会一直处于等待状态consume(queue.take());}} catch (InterruptedException ex) {}}// 实际的消费行为void consume(Object x) {System.out.println(x);}}

最后开启任务:

 public static int cout = 1;public static void main(String[] args) {BlockingQueue queue = new ArrayBlockingQueue(10);Producer pro = new Producer(queue);Consumer con = new Consumer(queue);// 开始生产new Thread(pro).start();// 开始消费new Thread(con).start();}

实际上,在很多场景都有这种生产者-消费者模式,比如在Volley中其就使用到了BlockingQueue来接收请求,然后获取任务从而发起请求。(注:生产者-消费者模式很经典,在操作系统中PV锁控制的中断也可以实现类似生产者-消费者模式)。

常用的阻塞队列

其实从上面的代码可能已经猜出来了,BlockingQueue只不过是一个接口,其下有众多的不同实现类,下面就简单介绍下其实现类以及使用的场景:

ArrayBlockingQueue

一个由数组支持的有限阻塞队列(其大小在创建的时候就被确定,并且不能在修改)。此队列里存储的元素顺序是FIFO(first-in-first-out),是一个很标准的普通队列,也是我们最常使用到的阻塞队列。其头部的元素是在队列带的时间最长,尾部元素在队列中呆的时间最短,新来的元素是插在尾部的,而当队列获取元素时是从头部获取的。如果试图将一个元素put到一个full状态的队列,这个操作就会被阻塞,直到队列有空位置。如果从一个empty队列获取新的元素同样也会被阻塞,知道有元素可获取。
    此类为等待的消费者/生产者提供了一个可选的公平的处理策略。此类默认的不保证阻塞的顺序。可以看到ArrayBlockingQueue有三个构造器,第一个就是只提供一个容量,第二个构造器的第一个参数就是容量,第二个参数就是是否公平加入/移除队列,如果设置为true就会按照FIFO的顺序来入队列和出队列,如果为false就不保证顺序了。一个参数的构造器默认的是false。

DelayQueue

此队列是一个无界限的队列,只有当他的元素在队列中超过规定时间了他才可以被取出来,其头部是超期时间最长的元素,尾部就是超期最短(或还未过期)的元素。如果还没有过期的元素,那么就不存在头部了,将直接返当回null,他的队列调用了元素的getDelay()方法返回的值小于或等于0就表示过期了。

这个队列不是所有的元素都可以放进去的,必须是实现了Delayed接口的类对象才可以放进去,否则就会报类型转换异常,那么什么是Delayed接口呢?它很简单,直接去看其API吧!我们暂时只需要知道如何使用就行!下面给出一个场景用DelayQueue实现:

作为上班族是不能迟到早退的,不然扣钱扣得那叫一个惨不忍睹....亲身体验,我有一次就年会那天直接去年会现场了,没有来打卡就扣了好几百啊有木有,就半天时间啊有木有,说多了都是泪...言归正传。有一天Boss让小A去加个班,并让小A不管工作有没有做完必须在晚上24点才能下班(我们先假设这个工作加班到死也一下子做不完),在小班后小A还得打卡验证。那么程序如何模拟呢?下面上代码:

(1)老板类

 /*** 老板* * @author Administrator* */public static class Boss implements Runnable {private final BlockingQueue queue;Boss(BlockingQueue q) {queue = q;}public void run() {try {// 告诉小A去加班啊,别偷懒,下班记得在24点打卡啊queue.put(produce());// }} catch (InterruptedException ex) {}}private Object produce() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");try {// 这里指定啥时候下班:老板让他在24点下班Date date = sdf.parse("2089-06-18 24:00:00");// 构造器传递一个long类型的数值return new DelayObject(date.getTime());} catch (ParseException e) {e.printStackTrace();}return null;}}

(2)小A

 /*** 存放到DelayQueue的元素必须实现Delay接口* * @author Administrator* */private static class 小A implements Delayed {static int count = 1;static long delay = 1;public 小A(long delay) {this.delay = delay;count++;}public int compareTo(Delayed o) {return 0;}/*** 队列会不停调用此方法(比如take()方法里有个死循环,会不断调用getDelay())判断该元素是否可以取出来,* 因此此参数不应该是一个固定的,应该和系统时间关联,即随着时间的推移 可以使得这个元素可以使用* 当这个返回值小于等于0的时候就可以从队列中取出这个元素了*/public long getDelay(TimeUnit unit) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 每次调用getDelay()时都重新计时Date date = new Date();String sd = sdf.format(date);try {Date d2 = sdf.parse(sd); // 前的时间long diff = delay - d2.getTime(); // 两时间差,精确到毫秒// 此diff会1秒秒的减小,当其为0的时候就不再调用了,直接就可以出队列了。return diff;} catch (ParseException e) {e.printStackTrace();}return 0;}/*** 下班了*/public void free() {System.out.println("哈哈哈哈哈哈---我下班了");}}

(3)考勤系统

 /*** 公司的考勤系统* * @author Administrator* */public static class SystemWorker implements Runnable {private final BlockingQueue queue;SystemWorker(BlockingQueue q) {queue = q;}public void run() {try {while (true) {// 只要还没到下班时间,他就一直处于阻塞状态,等待offWork(queue.take());}} catch (InterruptedException ex) {}}// 下班打卡:其参数就是具体的某个员工下班void offWork(Object x) {小A delay = (小A) x;// 如果能下班就说delay.free();}}

(4)正式运作

     BlockingQueue queue = new DelayQueue();Boss pro = new Boss(queue);SystemWorker con = new SystemWorker(queue);// 老板发话new Thread(pro).start();// 系统开始工作new Thread(con).start();

以上就是三个角色完成的上述场景,注意啊,小A这个类的命名我用了个中文哈(逼死强迫症  T_T )。好,如上就是DelayQueue的工作场景和使用方法了,这里总结下,首先主要就是确定未来时间点,也就是任务结束的时间,并且在getDelay()方法中动态的获取当前时间进行计算,不断地缩小差值,也就是说DelayQueue需要我们手动计算延时操作,这样我们可以在遇到突发状况时可以随时更改此时间差值以提前完成任务。

LinkedBlockingQueue

是一个基于连接节点的任意大小容量的队列,这个队列的顺序是FIFO,其头部是存入队列最早的元素,尾部是存入队列最晚的元素,每次都是讲元素插入尾部,从头部取出元素。LinkedBlockingQueue:比ArrayBlockingQueue有更大的吞吐量,但是在并发的情况下其性能是不可预测的。

好吧!暂时就介绍以上三种常见常用的队列,另外还有LinkedTransferQueue、PriorityBlockingQueue、SynchronousQueue三种大家可以自行查看API使用。

BlockingQueue!相关推荐

  1. Spring Boot + MDC 实现全链路调用日志跟踪,这才叫优雅!

    欢迎关注方志朋的博客,回复"666"获面试宝典 1.简介: MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j .logback及log4j ...

  2. redisTemplate分布式锁演变、redission分布式锁实现!

    以下文章来源方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/zhangkaixuan456 /article/details/110679617 分布式锁 ...

  3. 突然就懵了!面试官问我:线程池中多余的线程是如何回收的?

    点击关注公众号,Java干货及时送达 最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多 ...

  4. SpringCloud中Hystrix容错保护原理及配置,看它就够了!

    点击关注公众号,Java干货及时送达 作者:kosamino cnblogs.com/jing99/p/11625306.html 1 什么是灾难性雪崩效应? 如下图的过程所示,灾难性雪崩形成原因就大 ...

  5. 简单读!tomcat源码(一)启动与监听

    tomcat 作为知名的web容器,很棒! 本文简单了从其应用命令开始拆解,让我们对他有清晰的了解,揭开神秘的面纱!(冗长的代码流水线,给你一目了然) 话分两头: 1. tomcat是如何启动的? 2 ...

  6. 讲解 Redis 的一篇深度好文!

    目录 概述 Redis的数据结构和常用命令 数据持久化 内存管理与数据淘汰机制 Pipelining 事务与Scripting Redis性能调优 主从复制与集群分片 Redis Java客户端的选择 ...

  7. 探索JAVA并发 - 并发容器全家福!

    作者:acupt,专注Java,架构师社区合伙人! 14个并发容器,你用过几个? 不考虑多线程并发的情况下,容器类一般使用ArrayList.HashMap等线程不安全的类,效率更高.在并发场景下,常 ...

  8. java 判断当前时间是否为节假日_最近公司招人,研发组商量了下,暂时定下这么多java面试题!...

    一.List相关面试题 List是一个集合接口,分为AarrayList和LinkedList两个实现方式 ArrayList:底层实现就是数组,且ArrayList实现了RandomAccess,表 ...

  9. 阻塞队列BlockingQueue 学习

    import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Time ...

最新文章

  1. Oracle基础 动态SQL语句
  2. 百度李彦宏:人工智能是万物命脉
  3. 钱颖一:人工智能将使中国教育优势荡然无存
  4. [编写高质量代码:改善java程序的151个建议]建议69 列表相等只需关心元素相等...
  5. numpy.arcsin详解
  6. 【核心API开发】Spark入门教程[3]
  7. oracle备份慢,诊断Oracle RMAN备份慢的原因
  8. VTK:参数样条用法实战
  9. Spring Data对Cassandra 3的支持
  10. 信息学奥赛一本通 1129:统计数字字符个数 | OpenJudge NOI 1.7 01
  11. C语言实验源程序保存,实验一 C语言集成开发环境
  12. 诺基亚推出全新IMPACT平台,交付快捷安全的物联网服务
  13. win7计算器功能详解
  14. mui+hbuilder 将图片压缩并转变为base64 与改变图片尺寸大小来减小图片文件大小
  15. Biological Psychiatry:亚属连接预测经颅磁刺激位点抗抑郁疗效
  16. 05.SQL Server(高级查询)
  17. 矩形区域的泊松方程,深度学习模拟差分法
  18. Libre OJ P2332「JOI 2017 Final」焚风现象【差分思想】By cellur925
  19. 如何在博客插入UML图
  20. 中国四大发明是公认的

热门文章

  1. 如何编写 Runkeeper 一样的 app(2)
  2. 一年365天,每周5个工作日,进步1%,两个休息日退步1%,最终进步到多少
  3. 鱼和熊掌兼得——解密阿里云PCDN如何实现高质量低价格
  4. rn+android+sdk,RN与Android原生交互
  5. java8-stream新特性详解及实战
  6. DDR存储颗粒度大小
  7. 汉语拼音工具包相关实用方法
  8. 新娘结婚前8天遇害 新郎抱遗照殡仪馆办婚礼
  9. 用核显能跑matlab吗,5张图告诉你核显究竟能干嘛
  10. MySQL数据库————MVCC