H__D  

  阅读本文,请先了解PriorityQueue的使用(【Java】PriorityQueue 源码分析 ),以及AQS等

一、DelayQueue介绍

  一个无限制的blocking queue的Delayed元素,其中元素只能在其延迟到期时才被使用。 队列的Delayed元素,其延迟期满后保存时间。 如果没有延迟到期,那么没有头, poll会返回null 。 当元素的getDelay(TimeUnit.NANOSECONDS)方法返回小于或等于零的值时,就会发生getDelay(TimeUnit.NANOSECONDS) 。 即使未使用的元素不能使用takepoll ,它们另外被视为普通元素。 例如, size方法返回到期和未到期元素的计数。 此队列不允许空元素。

二、属性

 1 // 全局锁
 2 private final transient ReentrantLock lock = new ReentrantLock();
 3 
 4 // 优先级队列
 5 private final PriorityQueue<E> q = new PriorityQueue<E>();
 6 
 7 // 最先消费线程
 8 private Thread leader = null;
 9 
10 // 可用条件
11 private final Condition available = lock.newCondition();

三、方法

1、构造方法

1 // 创建一个空的阻塞式延迟队列
2 public DelayQueue() {}
3 
4 // 根据集合创建一个阻塞式延迟队列
5 public DelayQueue(Collection<? extends E> c) {
6     this.addAll(c);
7 }

2、offer() 方法

 1 // 放入元素到队列中 
 2 public boolean offer(E e) {
 3     final ReentrantLock lock = this.lock;
 4     // 获取锁
 5     lock.lock();
 6     try {
 7         // 调用数据优先级队列的 offer() 方法放入队列中
 8         q.offer(e);
 9         // 查看放入元素 与 优先级队列顶元素是否相同
10         if (q.peek() == e) {
11             leader = null;
12             // 通知其他线程,队列可用
13             available.signal();
14         }
15         return true;
16     } finally {
17         // 释放锁
18         lock.unlock();
19     }
20 }

3、peek() 方法

 1 // 获取但不取出,优先级队列中队列顶的元素
 2 public E peek() {
 3     final ReentrantLock lock = this.lock;
 4     lock.lock();
 5     try {
 6 
 7         return q.peek();
 8     } finally {
 9         lock.unlock();
10     }
11 }

3、take() 方法

 1 // 获取队列元素
 2 public E take() throws InterruptedException {
 3     final ReentrantLock lock = this.lock;
 4     // 获取可以被打断的锁
 5     lock.lockInterruptibly();
 6     try {
 7         for (;;) {
 8             // 查看队列头的元素
 9             E first = q.peek();
10             if (first == null)
11                 // 队列无用元素,线程等待可用
12                 available.await();
13             else {
14                 // 延迟时间
15                 long delay = first.getDelay(NANOSECONDS);
16                 // 不为空但是队头元素有过期
17                 if (delay <= 0)
18                     // 取出队列顶的元素,返回
19                     return q.poll();
20                 // 没有过期元素时
21                 first = null; // don't retain ref while waiting
22                 // 前面还有消费线程等待消费
23                 if (leader != null)
24                     available.await();
25                 else {
26                     // 设置本线程为最先消费线程
27                     Thread thisThread = Thread.currentThread();
28                     leader = thisThread;
29                     try {
30                         // 本线程等待 delay 时长,后被唤醒
31                         // delay 时长 后,队列头第一个元素会过期
32                         available.awaitNanos(delay);
33                     } finally {
34                         if (leader == thisThread)
35                             leader = null;
36                     }
37                 }
38             }
39         }
40     } finally {
41         // 如果 leader 为空,且队列不为空,唤醒一个消费线程
42         if (leader == null && q.peek() != null)
43 
44             available.signal();
45         // 释放锁
46         lock.unlock();
47     }
48 }

四、运行流程

  

  示例代码:

 1 public class TestDelayQueue {
 2 
 3 
 4     public static void main(String[] args) throws InterruptedException {
 5 
 6         DelayQueue<DelayedEle> queue = new DelayQueue();
 7 
 8         for (int i = 0; i < 5; i++) {
 9             int finalI = i;
10             new Thread(() -> {
11                 queue.offer(new DelayedEle((5-finalI) * 1000, "" + finalI));
12                 System.out.println("生产:" + finalI);
13             }).start();
14         }
15 
16         Thread.sleep(2000);
17 
18         for (int j = 0; j < 5; j++) {
19             int finalI = j;
20             new Thread(() -> {
21                 try {
22                     System.out.println("消费:" + queue.take());
23                 } catch (InterruptedException e) {
24                     e.printStackTrace();
25                 }
26             }).start();
27         }
28     }
29 }
30 
31 class DelayedEle implements Delayed {
32 
33     private final long delayTime; //延迟时间
34     private final long expire;  //到期时间
35     private String data;   //数据
36 
37     public DelayedEle(long delay, String data) {
38         delayTime = delay;
39         this.data = data;
40         expire = System.currentTimeMillis() + delay;
41     }
42 
43     /**
44      * 剩余时间=到期时间-当前时间
45      */
46     @Override
47     public long getDelay(TimeUnit unit) {
48         return unit.convert(this.expire - System.currentTimeMillis() , TimeUnit.MILLISECONDS);
49     }
50 
51     /**
52      * 优先队列里面优先级规则
53      */
54     @Override
55     public int compareTo(Delayed o) {
56         return (int) (this.getDelay(TimeUnit.MILLISECONDS) -o.getDelay(TimeUnit.MILLISECONDS));
57     }
58 
59     @Override
60     public String toString() {
61         final StringBuilder sb = new StringBuilder("DelayedElement{");
62         sb.append("delay=").append(delayTime);
63         sb.append(", expire=").append(expire);
64         sb.append(", data='").append(data).append('\'');
65         sb.append('}');
66         return sb.toString();
67     }
68 }

 

五、总结

  1、DelayQueue 底层使用了PriorityQueue来存储数据,存放的数据要求实现Delayed接口

  2、DelayQueue 是一个无界的阻塞队列。

  3、DelayQueue 队列中的元素只有延迟期满,才能从队列中被出去,

posted on 2021-03-30 17:27  H__D  阅读(120)  评论(0编辑  收藏  举报