简介

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

源码解析

属性

     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. 固定UILabel宽度分行显示

    固定UILabel宽度分行显示 这种小伎俩估计都被用烂了,笔者给大家提供一个category文件,供大家简单设置哦. 各种富文本效果哦(普通文本也是可以用的呢): 3行,固定宽度200 2行,固定宽度 ...

  2. mysql内存评估计算

    这是一个可以评估mysql内存使用大小的网站,根据一些基本的参数,来确定的. 网站:http://www.mysqlcalculator.com/

  3. Python(二)列表的增删改查

    一,列表的增删改查 列表中增加元素: 1,从列表的末尾增加一个元素:append("") 2,从列表中插入一个元素:insert(下标位置,插入的元素) 合并列表: 1,name. ...

  4. 树&二叉树&&满二叉树&&完全二叉树&&完满二叉树

    目录 树 二叉树 完美二叉树(又名满二叉树)(Perfect Binary Tree) 完全二叉树(Complete Binary Tree) 完满二叉树(Full Binary Tree) 树 名称 ...

  5. 原生js实现一个DIV的碰撞反弹运动

     原生js实现一个DIV的碰撞反弹运动: 关键在于DIV的边界检测,进而改变运动方向,即可实现碰撞反弹效果. <!DOCTYPE html> <html lang="en& ...

  6. PHP获取视频的第一帧与时长

    //获得视频文件的缩略图 function getVideoCover($file,$time,$name) { if(empty($time))$time = '1';//默认截取第一秒第一帧 $s ...

  7. shell基础--shell特殊变量

    一.变量列表 二.实验 系统:centos 7 1.特殊变量 [root@~_~ day2]# cat p.sh #!/bin/bashecho '$0:'$0echo '$*:'$*echo '$@ ...

  8. 【转】 Android应用内多进程分析和研究

    正常情况下,一个apk启动后只会运行在一个进程中,其进程名为AndroidManifest.xml文件中指定的应用包名,所有的基本组件都会在这个进程中运行.但是如果需要将某些组件(如Service.A ...

  9. PHP microtime() 函数

    定义和用法 microtime() 函数返回当前 Unix 时间戳和微秒数. 语法 microtime(get_as_float) 参数 描述 get_as_float 如果给出了 get_as_fl ...

  10. Day11 Java内部类

    什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public ...