【JUC源码解析】DelayQueue
简介
基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首。只有过期的元素才能出队,在此之前,线程等待。
源码解析
属性
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的更多相关文章
- 【JUC源码解析】ScheduledThreadPoolExecutor
		简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ... 
- 【JUC源码解析】SynchronousQueue
		简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ... 
- 【JUC源码解析】ForkJoinPool
		简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ... 
- 【JUC源码解析】CyclicBarrier
		简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ... 
- 【JUC源码解析】ConcurrentLinkedQueue
		简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ... 
- 【JUC源码解析】Exchanger
		简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ... 
- Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
		功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ... 
- Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
		功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ... 
- Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
		功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ... 
随机推荐
- 固定UILabel宽度分行显示
			固定UILabel宽度分行显示 这种小伎俩估计都被用烂了,笔者给大家提供一个category文件,供大家简单设置哦. 各种富文本效果哦(普通文本也是可以用的呢): 3行,固定宽度200 2行,固定宽度 ... 
- mysql内存评估计算
			这是一个可以评估mysql内存使用大小的网站,根据一些基本的参数,来确定的. 网站:http://www.mysqlcalculator.com/ 
- Python(二)列表的增删改查
			一,列表的增删改查 列表中增加元素: 1,从列表的末尾增加一个元素:append("") 2,从列表中插入一个元素:insert(下标位置,插入的元素) 合并列表: 1,name. ... 
- 树&二叉树&&满二叉树&&完全二叉树&&完满二叉树
			目录 树 二叉树 完美二叉树(又名满二叉树)(Perfect Binary Tree) 完全二叉树(Complete Binary Tree) 完满二叉树(Full Binary Tree) 树 名称 ... 
- 原生js实现一个DIV的碰撞反弹运动
			原生js实现一个DIV的碰撞反弹运动: 关键在于DIV的边界检测,进而改变运动方向,即可实现碰撞反弹效果. <!DOCTYPE html> <html lang="en& ... 
- PHP获取视频的第一帧与时长
			//获得视频文件的缩略图 function getVideoCover($file,$time,$name) { if(empty($time))$time = '1';//默认截取第一秒第一帧 $s ... 
- shell基础--shell特殊变量
			一.变量列表 二.实验 系统:centos 7 1.特殊变量 [root@~_~ day2]# cat p.sh #!/bin/bashecho '$0:'$0echo '$*:'$*echo '$@ ... 
- 【转】 Android应用内多进程分析和研究
			正常情况下,一个apk启动后只会运行在一个进程中,其进程名为AndroidManifest.xml文件中指定的应用包名,所有的基本组件都会在这个进程中运行.但是如果需要将某些组件(如Service.A ... 
- PHP microtime() 函数
			定义和用法 microtime() 函数返回当前 Unix 时间戳和微秒数. 语法 microtime(get_as_float) 参数 描述 get_as_float 如果给出了 get_as_fl ... 
- Day11 Java内部类
			什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public ... 
