延时阻塞队列DelayQueue是一种特殊的优先级队列,它也是无界的,它要求每个元素都实现Delayed接口,该接口的声明为:
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
Delayed扩展了Comparable接口,也就是说,DelayQueue的每个元素都是可比较的,它有一个额外方法getDelay返回一个给定时间单位unit的整数,表示再延迟多长时间,如果小于等于0,表示不再延迟。
DelayQueue也是优先级队列,它按元素的延时时间出队,它的特殊之处在于,只有当元素的延时过期之后才能被从队列中拿走,也就是说,take方法总是返回第一个过期的元素,如果没有,则阻塞等待。
DelayQueue可以用于实现定时任务,我们看段简单的示例代码:
public class DelayedQueueDemo {
private static final AtomicLong taskSequencer = new AtomicLong(0);
static class DelayedTask implements Delayed {
private long runTime;
private long sequence;
private Runnable task;
public DelayedTask(int delayedSeconds, Runnable task) {
this.runTime = System.currentTimeMillis() + delayedSeconds * 1000;
this.sequence = taskSequencer.getAndIncrement();
this.task = task;
}
@Override
public int compareTo(Delayed o) {
if (o == this) {
return 0;
}
if (o instanceof DelayedTask) {
DelayedTask other = (DelayedTask) o;
if (runTime < other.runTime) {
return -1;
} else if (runTime > other.runTime) {
return 1;
} else if (sequence < other.sequence) {
return -1;
} else {
return 1;
}
}
throw new IllegalArgumentException();
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(runTime - System.currentTimeMillis(),
TimeUnit.MICROSECONDS);
}
public Runnable getTask() {
return task;
}
}
public static void main(String[] args) throws InterruptedException {
DelayQueue<DelayedTask> tasks = new DelayQueue<>();
tasks.put(new DelayedTask(2, new Runnable() {
@Override
public void run() {
System.out.println("execute delayed task");
}
}));
DelayedTask task = tasks.take();
task.getTask().run();
}
}
DelayedTask表示延时任务,只有延时过期后任务才会执行,任务按延时时间排序,延时一样的按照入队顺序排序。
内部,DelayQueue是基于PriorityQueue实现的,它使用一个锁ReentrantLock保护所有访问,使用一个条件available表示头部是否有元素,当头部元素的延时未到时,take操作会根据延时计算需睡眠的时间,然后睡眠,如果在此过程中有新的元素入队,且成为头部元素,则阻塞睡眠的线程会被提前唤醒然后重新检查。以上是基本思路,DelayQueue的实现有一些优化,以减少不必要的唤醒,具体我们就不探讨了。
模拟一个考试的日子,考试时间为120分钟,30分钟后才可交卷,当时间到了,或学生都交完卷了考试结束。
这个场景中几个点需要注意:
抽象出两个类,学生类和老师类,用DelayQueue存储考生(Student类)。每一个考生都有自己的名字和完成试卷的时间
Teacher线程对DelayQueue进行监控,收取完成试卷小于120分钟的学生的试卷。当考试时间120分钟到时,teacher线程宣布考试结束,强制DelayQueue中还存在的考生交卷。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
package
zhongqiu.common.base.thread;
import
java.util.Iterator;
import
java.util.Random;
import
java.util.concurrent.DelayQueue;
import
java.util.concurrent.Delayed;
import
java.util.concurrent.TimeUnit;
public
class
DelayQueueDemoOne {
public
static
void
main(String[] args)
throws
InterruptedException {
// TODO Auto-generated method stub
int
studentNumber =
20
;
DelayQueue<Student> students =
new
DelayQueue<Student>();
Random random =
new
Random();
for
(
int
i =
0
; i < studentNumber; i++) {
students.put(
new
Student(
"student"
+ (i +
1
),
30
+ random.nextInt(
120
)));
}
students.put(
new
Student(
"student"
,
120
));
Thread teacherThread =
new
Thread(
new
Teacher(students));
teacherThread.start();
}
}
class
Student
implements
Runnable, Delayed {
private
String name;
public
long
workTime;
private
long
submitTime;
private
boolean
isForce =
false
;
public
Student() {
}
public
Student(String name,
long
workTime) {
this
.name = name;
this
.workTime = workTime;
this
.submitTime = TimeUnit.NANOSECONDS.convert(workTime, TimeUnit.NANOSECONDS) + System.nanoTime();
// 纳秒级别
}
@Override
public
int
compareTo(Delayed o) {
// TODO Auto-generated method stub
if
(o ==
null
|| !(o
instanceof
Student))
return
1
;
if
(o ==
this
)
return
0
;
Student s = (Student) o;
if
(
this
.workTime > s.workTime) {
return
1
;
}
else
if
(
this
.workTime == s.workTime) {
return
0
;
}
else
{
return
-
1
;
}
}
@Override
public
long
getDelay(TimeUnit unit) {
// TODO Auto-generated method stub
return
unit.convert(submitTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
@Override
public
void
run() {
// TODO Auto-generated method stub
if
(isForce) {
System.out.println(name +
" 交卷,实际用时 120分钟"
);
}
else
{
System.out.println(name +
" 交卷,"
+
"实际用时 "
+ workTime +
" 分钟"
);
}
}
public
boolean
isForce() {
return
isForce;
}
public
void
setForce(
boolean
isForce) {
this
.isForce = isForce;
}
}
class
Teacher
implements
Runnable {
private
int
counter =
20
;
private
DelayQueue<Student> students;
public
Teacher(DelayQueue<Student> students) {
this
.students = students;
}
@Override
public
void
run() {
// TODO Auto-generated method stub
try
{
System.out.println(
" test start"
);
while
(counter >
0
) {
Student student = students.poll();
if
(student.workTime<
120
) {
student.run();
if
(counter >
0
) {
counter--;
}
}
else
{
System.out.println(
" 考试时间到,全部交卷!"
);
Student tmpStudent;
for
(Iterator<Student> iterator2 = students.iterator(); iterator2.hasNext();) {
tmpStudent = iterator2.next();
tmpStudent.setForce(
true
);
tmpStudent.run();
if
(counter >
0
) {
counter--;
}
}
}
}
}
catch
(Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
|