简介

基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首。只有过期的元素才能出队,在此之前,线程等待。

源码解析

属性

     private final transient ReentrantLock lock = new ReentrantLock(); // 可重入锁
private final PriorityQueue<E> q = new PriorityQueue<E>(); // 优先级队列
private Thread leader = null; // 领导者线程,成为领导者的线程,只会等待队首元素的delay时间,其他线程会一直等待,直到有线程唤醒它,一直等待的这些线程会伴随着一个个元素的出队相继成为leader,同一时刻,只有一个线程作为领导者等待一个delay时间
private final Condition available = lock.newCondition(); // 条件队列,阻塞的线程会被压入条件队列,等待被唤醒

构造方法

     public DelayQueue() {
} public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}

关键方法

offer(E e)

     public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); // 获取锁
try {
q.offer(e); // 入队
if (q.peek() == e) { // 队首元素更新为新加入的元素
leader = null; // 上一个领导者线程同其他线程一样被同等对待(等着被唤醒),重新抢占leader,其实不必抢占(因为只有一个线程被唤醒),只不过leader线程等待的delay需要更新(新的队首元素的delay值)
available.signal(); // 只会唤醒Condition队列的队首线程
}
return true;
} finally {
lock.unlock(); // 释放锁
}
}

take()

     public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); // 响应中断
try {
for (;;) {
E first = q.peek(); // 取出队首元素
if (first == null) // 若为空
available.await(); // 说明队列为空,线程等待
else {
long delay = first.getDelay(NANOSECONDS); // 否则,获取first的delay值
if (delay <= 0) // 若已到期
return q.poll(); // 弹出此元素
first = null; // 线程等待时,不保持对元素的引用,
if (leader != null) // 领导者线程不为空,则等待
available.await();
else {
Thread thisThread = Thread.currentThread(); // 否则,设置当前线程为领导者线程
leader = thisThread;
try {
available.awaitNanos(delay); // 等待delay时间
} finally {
if (leader == thisThread) // 等待结束后,如果自己是领导者线程,重置leader为null
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null) // 如果自己是领导线程(上面把leader置为null了),并且有元素,则唤醒其他线程(其中一个,下一届的leader,如此循环下去)
available.signal();
lock.unlock(); // 释放锁
}
}

leader线程,leader-follower模式,当真省去了不必要的出队入队操作(出入的是Condition的队列,出入者是唤醒又马上要等待的线程)

每一届的leader线程对应当前的队首元素,当这届leader线程拿到元素后,会空出leader位置,并唤醒其中等待时间最长的线程成为下一届的leader,如此传递下去。

行文至此结束。

尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_delayqueue.html

【JUC源码解析】DelayQueue的更多相关文章

  1. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  2. 【JUC源码解析】SynchronousQueue

    简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...

  3. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  4. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

  5. 【JUC源码解析】ConcurrentLinkedQueue

    简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ...

  6. 【JUC源码解析】Exchanger

    简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...

  7. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  8. Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue

    功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...

  9. Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer

    功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...

随机推荐

  1. Git提交代码自动触发JenKins构建项目

    1.需求场景 用户提交代码后自动触发jenkins构建项目 流程图如下: 2.JenKins安装Gitlab Hook Plugin插件 3.JenKins配置 4.Gitlab Hook Plugi ...

  2. css实现梯形

    使用伪元素before和after分别在矩形元素前后加三角形或者直接设置border 使用3d旋转矩形,使之看起来像矩形 <html> <head> <meta char ...

  3. javascript unshift()和shift()

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  4. [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform

    [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...

  5. Fix for: Permission denied to access property 'toString'

    Originally posted by rwolffgang here. Hi guys,when developing a game that runs in an iframe (Faceboo ...

  6. 【问题记录】python 函数 传入一个对象返回一个对象值得注意

    写了一个函数,这个函数接收一个参数,在函数里面判断这个参数是否为None或者不合法状态, 如果处于不合法状态,则创建一个对象返回, 如果合法直接返回 代码示例如下: def get_mq_connec ...

  7. 在远程登陆的主机上通过命令行源码编译安装 GNU M4、autoconf、automake 等程序

    由于实验需要,最近获得了一个实验室服务器的账号,平常主要通过 ssh 进行远程登陆进行实验.一方面,远程登录的机器只提供终端界面,一般只通过命令行进行任务操作:另一方面,由于是多人共享服务器,故而个人 ...

  8. 使用Charles进行移动APP抓包分析

    一.简介 Charles是目前最强大最流行的http抓包调试工具,Mac.Unix.Windows各个平台都支持.特别是做APP开发,调试与服务端的通信,Charles是必备工具. 目前Charles ...

  9. P1481 魔族密码

    题目描述 风之子刚走进他的考场,就…… 花花:当当当当~~偶是魅力女皇——花花!!^^(华丽出场,礼炮,鲜花) 风之子:我呕……(杀死人的眼神)快说题目!否则……-_-### 花花:……咦好冷我们现在 ...

  10. 为什么can接口要接通用CAN隔离收发器啊?

    为什么can接口要接通用CAN隔离收发器啊? 主要是为了实现远端接地环路断开的系统,让接口允许在很大的共模电压变化下保证可靠通讯.通常使用CAN通讯的系统,都是要实现远距离可靠通讯,这也恰恰是CAN- ...