转载请注明源出处:http://www.cnblogs.com/lighten/p/7493735.html

1.前言

  本章介绍阻塞队列DelayQueue,这是一个无界阻塞队列。其存储延时的元素,只有延时耗尽元素才能被取出。队列头元素就是最先耗尽延时的元素,如果没有元素耗尽延时,poll操作会返回null。同样的,该队列不允许空元素。针对延时的特性,可以用户定时任务,到达时间就能取出任务执行,设计有时效的缓存,超时就清除。

2.DelayQueue

2.1 数据结构

  该类的实现十分简单,可以说是站在其它类的肩膀上实现的。一个保证线程安全的锁,一个存储元素的优先队列,一个等待队列头元素的线程,一个触发条件。

  优先队列之前介绍过,队列头是比较器或元素自身的比较方法比较出来的最小元素。这个队列所有的元素都必须实现Delayed接口,其方法需要给出剩余的延时时间。Delayed接口又是实现Comparable接口,注意该优先队列就是使用这个方法进行对比的,所以Comparable的实现要借助Delayed接口的方法选出剩余延时小,才能保证使用正确。

2.2 基本操作

  放入一个Delayed元素:

  线程安全,先加锁,然后放入一个元素到优先队列中。尝试取出,如果还是它,头元素被替换了,需要重置等待头元素的线程,唤醒阻塞的线程。

  取出元素也先加锁,尝试取出,如果时间未到返回null,否则就取出。

  超时的poll方法,以纳秒计算等待时间。先加锁保证线程安全,无限循环取第一个元素,如果没取到并且等待时间耗尽,直接返回null,没耗尽等待时间就继续等,直到被可用条件唤醒,或等待超时自动唤醒。如果取到了第一个元素,还需要看其剩余时间是否够了,够了就直接返回。不够等待时间又耗尽了就返回null。如果剩余等待时间比元素满足条件时间要小或者是有线程在等待第一个元素,直接等下去,等完剩余时间。否则,当前线程就是等待头元素的线程,可以等到头元素,等待头元素的时间。

  整个类的实现并不难,leader线程感觉没什么作用,但是按照JDK的注释所说,这种领导跟随者模式可以最小化等待的时间。就是说leader线程可以等待下一个延时,其它线程需要无限等待。看代码其实也能明白,如果存在leader线程,一定是有一个线程在等待第一个元素,那么本线程就必须一直等,而拿到leader的线程,只需要等第一个元素剩余的延时就可以了。

3.使用例子

  定义一个Delayed接口实现类:

  1. class DelayedObject implements Delayed {
  2.  
  3. private long delay;
  4. private long begin;
  5. private int value;
  6.  
  7. public DelayedObject(int value, long delaySecond) {
  8. this.begin = System.nanoTime();
  9. this.delay = delaySecond;
  10. this.value = value;
  11. }
  12.  
  13. @Override
  14. public int compareTo(Delayed o) {
  15. return getDelay(TimeUnit.NANOSECONDS) > o.getDelay(TimeUnit.NANOSECONDS) ? 1 :
  16. (getDelay(TimeUnit.NANOSECONDS) == o.getDelay(TimeUnit.NANOSECONDS) ? 0 : -1);
  17. }
  18.  
  19. @Override
  20. public long getDelay(TimeUnit unit) {
  21. return unit.convert(delay, TimeUnit.SECONDS) - System.nanoTime() + begin;
  22. }
  23.  
  24. public int getValue() {
  25. return value;
  26. }
  27. }

  测试用例:

  1.   @Test
  2. public void test() {
  3. long begin = System.currentTimeMillis();
  4. DelayQueue<DelayedObject> queue = new DelayQueue<>();
  5. new Thread(new Runnable() {
  6. public void run() {
  7. for(int i = 0; i < 10; i++) {
  8. queue.offer(new DelayedObject(i, 9-i/2));
  9. }
  10. }
  11. }).start();
  12.  
  13. new Thread(new Runnable() {
  14. @Override
  15. public void run() {
  16. try {
  17. while(true) {
  18. DelayedObject object = queue.take();
  19. if(object != null) {
  20. System.out.println(Thread.currentThread().getName() + ":"+ object.getValue() + ",耗时:"
  21. + (System.currentTimeMillis()-begin)/1000);
  22. } else {
  23. break;
  24. }
  25. }
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. },"poll-1").start();
  31.  
  32. new Thread(new Runnable() {
  33. @Override
  34. public void run() {
  35. try {
  36. while(true) {
  37. DelayedObject object = queue.take();
  38. if(object != null) {
  39. System.out.println(Thread.currentThread().getName() + ":"+ object.getValue() + ",耗时:"
  40. + (System.currentTimeMillis()-begin)/1000);
  41. } else {
  42. break;
  43. }
  44. }
  45. } catch (InterruptedException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. },"poll-2").start();
  50. try {
  51. System.in.read();
  52. } catch (IOException e) {
  53. e.printStackTrace();
  54. }
  55. }

  测试结果:

  注意结果不一定和上图相同。取出顺序和System.out的顺序不一定一致,上面的结果是一个较符合顺序的结果。

Java之集合(十八)DelayQueue的更多相关文章

  1. Java进阶(三十八)快速排序

    Java进阶(三十八)快速排序 前言 有没有既不浪费空间又可以快一点的排序算法呢?那就是"快速排序"啦!光听这个名字是不是就觉得很高端呢. 假设我们现在对"6 1 2 7 ...

  2. Java IO(十八) BufferedReader 和 BufferedWriter

    Java IO(十八) BufferedReader 和 BufferedWriter 一.介绍 BufferedReader 和 BufferedWriter 是字符缓冲流,分别继承自 Reader ...

  3. 王颖奇 201771010129《面向对象程序设计Java》第十八周实验总结

    实验十八  总复习 实验时间 2018-12-30 1.实验目的与要求 (1) 综合掌握java基本程序结构: (2) 综合掌握java面向对象程序设计特点: (3) 综合掌握java GUI 程序设 ...

  4. Java基础(十八)集合(5)Queue集合

    队列是只能在尾部添加元素,同时只能在头部删除元素的数据结构.队列的原则就是“先进先出”. Queue接口是Collection接口的最后一个子接口. Queue接口是队列接口,而Deque接口是Que ...

  5. Java并发(十八):阻塞队列BlockingQueue

    阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用. 阻塞队列常用于生产 ...

  6. java提高篇(十八)-----数组之一:认识JAVA数组

          噢,它明白了,河水既没有牛伯伯说的那么浅,也没有小松鼠说的那么深,只有自己亲自试过才知道!道听途说永远只能看到表明现象,只有亲自试过了,才知道它的深浅!!!!! 一.什么是数组      ...

  7. Java笔记(二十八)……IO流下 IO包中其他常用类以及编码表问题

    PrintWriter打印流 Writer的子类,既可以接收字符流,也可以接收字节流,还可以接收文件名或者文件对象,非常方便 同时,还可以设置自动刷新以及保持原有格式写入各种文本类型的print方法 ...

  8. 夯实Java基础(十八)——泛型

    1.什么是泛型 泛型是Java1.5中出现的新特性,也是最重要的一个特性.泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类. ...

  9. java基础(十八)IO流(一)

    这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...

随机推荐

  1. Linux下编译与调试

    gcc/g++编译器 对于.c格式的C文件,可以采用gcc或g++编译 对于 .cc..cpp格式的C++文件,应该采用g++进行编译 常用的选项: -c  表示编译源文件 -o  表示输出目标文件 ...

  2. java中File的delete和deleteOnExit区别(转)

    Java的File类中有两个delete方法:delete和deleteOnExit delete无需解释,为直接删除,deleteOnExit文档解释为:在虚拟机终止时,请求删除此抽象路径名表示的文 ...

  3. 一个简单的Linux后门程序的实现

    该程序实质是一个简单的socket编程,在受害方上运行攻击代码(后门进程),通过socket打开一个预设端口,并监听,等待攻击方的链接.一旦攻击方通过网络链接工具试图链接该socket,那么后门进程立 ...

  4. python操作数据库-数据表

    数据表: 数据类型: 帮助的三种形式: 在cmd中输入: help 要帮助的主题词,或 ? 要帮助的主题词 或  \h 要帮助的主题词 . 数据表的创建: CREATE database IF NOT ...

  5. UltraEdit配置

    1.如何在vivado中调用UltraEdit 1.语法高亮 支持不同的编程语言,但是要添加相就的文件,这样不同语言的关键字就可以高亮显示. 在高级-> 配置 –> 语法高亮,选择文档 2 ...

  6. const define static extern 关键词详解

    const const关键词并不能把一个变量变成一个常量, 在符号前加上const表示这个符号不能被赋值, 即他的值对这个符号来说是只读的, 但并不代表这个值不能用其他方法去改变. 通过下面的例子就能 ...

  7. noip第7课作业

    1.    求平均值 [问题描述] 在一次运动会方队表演中,学校安排了十名老师进行打分.对于给定的每个参赛班级的不同打分(百分制整数),按照去掉一个最高分.去掉一个最低分,再算出平均分的方法,得到改班 ...

  8. HDU1501 Zipper(DFS) 2016-07-24 15:04 65人阅读 评论(0) 收藏

    Zipper Problem Description Given three strings, you are to determine whether the third string can be ...

  9. spring mvc后台如何处理ajax的请求,并返回json

    spring mvc中有一个注解:@ResponseBody,把这个注解放到ajax请求对应的@RequestMapping(xxx)方法上,在方法体内部,把要返回的json赋给一个String类型的 ...

  10. Java中的时间日期处理

    程序就是输入——>处理——>输出.对数据的处理是程序员需要着重注意的地方,快速.高效的对数据进行处理时我们的追求.其中,时间日期的处理又尤为重要和平凡,此次,我将把Java中的时间日期处理 ...