转载请注明源出处: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接口实现类:

class DelayedObject implements Delayed {

	private long delay;
private long begin;
private int value; public DelayedObject(int value, long delaySecond) {
this.begin = System.nanoTime();
this.delay = delaySecond;
this.value = value;
} @Override
public int compareTo(Delayed o) {
return getDelay(TimeUnit.NANOSECONDS) > o.getDelay(TimeUnit.NANOSECONDS) ? 1 :
(getDelay(TimeUnit.NANOSECONDS) == o.getDelay(TimeUnit.NANOSECONDS) ? 0 : -1);
} @Override
public long getDelay(TimeUnit unit) {
return unit.convert(delay, TimeUnit.SECONDS) - System.nanoTime() + begin;
} public int getValue() {
return value;
}
}

  测试用例:

  @Test
public void test() {
long begin = System.currentTimeMillis();
DelayQueue<DelayedObject> queue = new DelayQueue<>();
new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 10; i++) {
queue.offer(new DelayedObject(i, 9-i/2));
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
DelayedObject object = queue.take();
if(object != null) {
System.out.println(Thread.currentThread().getName() + ":"+ object.getValue() + ",耗时:"
+ (System.currentTimeMillis()-begin)/1000);
} else {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"poll-1").start(); new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
DelayedObject object = queue.take();
if(object != null) {
System.out.println(Thread.currentThread().getName() + ":"+ object.getValue() + ",耗时:"
+ (System.currentTimeMillis()-begin)/1000);
} else {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"poll-2").start();
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}

  测试结果:

  注意结果不一定和上图相同。取出顺序和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. python面向对象开发的自我理解

    ​详细代码理解可以参考 笨鸟教程博客:http://www.runoob.com/python3/python3-class.html 面向对象经常被提起,那到底什么是面向对象呢? 它的基本概念:类, ...

  2. IntelliJ IDEA 2017版 springloaded实现热部署

    1.配置pom.xml文档 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&qu ...

  3. Windows10和CentOS7双系统安装的一些小技巧

    我个人是先安装好了win10系统,且win10是单独在一个120g的盘里:而centOS7则是安装在另一个500g的磁盘的其中的380g里: 这里要着重注意的是,500g里分成380g的盘不要在win ...

  4. 201709019工作日记--Java中的各种锁--未解决

    1. Syncronized与ReentrantLock Synchronized比ReentrantLock进java标准早,因此一开始大家都是用它.相当于Java提供了一种封装的互斥锁机制,对于用 ...

  5. Scrapy爬取美女图片第三集 代理ip(上) (原创)

    首先说一声,让大家久等了.本来打算那天进行更新的,可是一细想,也只有我这样的单身狗还在做科研,大家可能没心思看更新的文章,所以就拖到了今天.不过忙了521,522这一天半,我把数据库也添加进来了,修复 ...

  6. HDU 1166敌兵布阵 2016-09-14 18:58 89人阅读 评论(0) 收藏

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  7. Windows 7下通过Excel2007连接Oracle数据库并对表查询

    http://blog.csdn.net/pan_tian/article/details/8133668 1. 环境变量的设置 1.1  ORACLE_HOME环境变量的设置,我这里指向了我的Ora ...

  8. 【WinRT】TransitionDemo

    折腾了一晚,将 WinRT 里的所有 Transition 做了一个小 Demo,用于演示各个 Transition 的效果. PS:NavigationThemeTransition 请参考:htt ...

  9. Oracle数据库多行记录转换一行并排序函数

    Oracle数据库多行记录转换一行并排序方法 在ORACLE数据库查询中,我们通常会要求用到将多行记录转换成一行并排序,这时候我们自然会想到Oracle的一个“wx_concat”函数,可以将多行记录 ...

  10. asp.net—工厂模式

    一.什么是工厂模式 定义:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类. 二.怎么使用工厂模式 首先模拟一个场景:有一个汽车工厂,  可以日本车.美国车.中国车... 这个场景怎么用工厂 ...