java并发容器——延迟队列DelayQueue

延时阻塞队列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分钟后才可交卷,当时间到了,或学生都交完卷了考试结束。

这个场景中几个点需要注意:

  1. 考试时间为120分钟,30分钟后才可交卷,初始化考生完成试卷时间最小应为30分钟
  2. 对于能够在120分钟内交卷的考生,如何实现这些考生交卷
  3. 对于120分钟内没有完成考试的考生,在120分钟考试时间到后需要让他们强制交卷
  4. 在所有的考生都交完卷后,需要将控制线程关闭

抽象出两个类,学生类和老师类,用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();
         }
     }
 
}

你可能感兴趣的:(java,延迟队列)