Java之集合(十八)DelayQueue
转载请注明源出处: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的更多相关文章
- Java进阶(三十八)快速排序
Java进阶(三十八)快速排序 前言 有没有既不浪费空间又可以快一点的排序算法呢?那就是"快速排序"啦!光听这个名字是不是就觉得很高端呢. 假设我们现在对"6 1 2 7 ...
- Java IO(十八) BufferedReader 和 BufferedWriter
Java IO(十八) BufferedReader 和 BufferedWriter 一.介绍 BufferedReader 和 BufferedWriter 是字符缓冲流,分别继承自 Reader ...
- 王颖奇 201771010129《面向对象程序设计Java》第十八周实验总结
实验十八 总复习 实验时间 2018-12-30 1.实验目的与要求 (1) 综合掌握java基本程序结构: (2) 综合掌握java面向对象程序设计特点: (3) 综合掌握java GUI 程序设 ...
- Java基础(十八)集合(5)Queue集合
队列是只能在尾部添加元素,同时只能在头部删除元素的数据结构.队列的原则就是“先进先出”. Queue接口是Collection接口的最后一个子接口. Queue接口是队列接口,而Deque接口是Que ...
- Java并发(十八):阻塞队列BlockingQueue
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用. 阻塞队列常用于生产 ...
- java提高篇(十八)-----数组之一:认识JAVA数组
噢,它明白了,河水既没有牛伯伯说的那么浅,也没有小松鼠说的那么深,只有自己亲自试过才知道!道听途说永远只能看到表明现象,只有亲自试过了,才知道它的深浅!!!!! 一.什么是数组 ...
- Java笔记(二十八)……IO流下 IO包中其他常用类以及编码表问题
PrintWriter打印流 Writer的子类,既可以接收字符流,也可以接收字节流,还可以接收文件名或者文件对象,非常方便 同时,还可以设置自动刷新以及保持原有格式写入各种文本类型的print方法 ...
- 夯实Java基础(十八)——泛型
1.什么是泛型 泛型是Java1.5中出现的新特性,也是最重要的一个特性.泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类. ...
- java基础(十八)IO流(一)
这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...
随机推荐
- 第六章 副词(Les adverbes )
副词属于不变词类,无性.数变化(tout除外),它的功能是修饰动词.形容词.副词或句子. ➡副词的构成 ⇨单一副词 bien tard hier mal vite tôt très souvent ...
- 打开子页面及刷新父页面 window.open window.opener.reload()
//打开子页面 var url=children_url;window.open(url) //刷新parent页面 var url=parent_urlfunction refresh(url){ ...
- SetFocus (GetDlgItem (hwnd, idFocus))函数的各参数的具体含义
Setfocus(HWMD hwnd):将窗口hwnd设置成获得焦点 GetDlgItem (hwnd, idFocus):此函数返回一个句柄 具体参数的含义: hwnd:包含该窗口标志位的对话框的句 ...
- Android-创建启动线程的两种方式
方式一:成为Thread的子类,然后在Thread的子类.start 缺点:存在耦合度(因为线程任务run方法里面的业务逻辑 和 线程启动耦合了) 缺点:Cat extends Thread {} 后 ...
- ASP.NET下使用Combres对JS、CSS合并和压缩
记录一下,如何简单快捷压缩js和css,通过合并来减少请求次数. 用到的网址: http://www.nuget.org/packages/combres/ https://github.com/bu ...
- C#之使用AutoUpdater自动更新客户端
安装NuGet包 在Visio studio中右击解决方案,选择管理NuGet包,搜索安装Autoupdater.NET.Official. 工作简介 从服务器下载包含更新文件的XML文件,从中获取软 ...
- 【cocos2d-x 仙凡奇缘-网游研发(1) 登录&注册】
转载请注明出处:http://www.cnblogs.com/zisou/p/xianfan01.html 公司的项目总算告一段落了,年前憋到年后,总算要上线了,所以我也有了时间来搞我自己的游戏项目了 ...
- DI spring.net简单使用
IOC或DI spring.net简单使用 一.spring.net是什么? Spring 框架本是 Java 平台上一个应用非常多的.开源的框架.虽然语言是固定的,但是好的方法应该是通用的,于是 ...
- AOP面向切面的基石——动态代理(一)
其实动态代理在Java里不是什么新技术了,早在java 1.2之后便通过 java.lang.reflect.InvocationHandler 加入了动态代理机制. 下面例子中,LancerEvol ...
- 1047 行 MySQL 详细学习笔记
https://blog.csdn.net/baidu_25310663/article/details/86517610 Windows服务 -- 启动MySQL net start mys ...