延迟队列DelayQueue take() 源码分析

  • 在工作中使用了延迟队列,对其内部的实现很好奇,于是就研究了一下其运行原理,在这里就介绍一下take()方法的源码

1 take()源码 如下所示

public E take() throws InterruptedException {
// 加锁的一个动作 保证获取数据的安全性
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
//peek 方法是去头部数据即第一个数据
E first = q.peek();
if (first == null)
//说明队列为空 调用condition.await()方法,会使得当前线程释放lock然后加入到等待队列中
available.await();
else {
//如果第一个数据不为空 获取消息体的延迟时间(getDelay() 会在消息体内重写 自定义添加延迟时间)
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
//如果延迟时间 小于等于0 说明已经达到了延迟时间 调用poll方法返回消息体
return q.poll();
//如果延迟时间不为空的话 说明还需要等待一段时间 此时重新循环 所以讲frist置为空
first = null;
if (leader != null)
//这里用到了Leader/Followers模式 有兴趣的话可以去百度一下这个模式
//如果leader 不为空 说明已经有线程在监听 即有线程在优先获取队列的首元素
//释放当前线程获取的锁 加入到等待队列中 即 当前线程变成了Followers
available.await();
else {
//如果没有leader 说明没有线程在监听(没有线程在优先获取队列的首元素)
// 将当前线程置为leader线程
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//让当前线程最长等待 delay 时间 等待
available.awaitNanos(delay);
} finally {
//释放leader权限
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 如果leader 为空 且 队列中有数据 说明没有其他线程在在等待
if (leader == null && q.peek() != null)
//唤醒睡眠的线程
available.signal();
//释放锁
lock.unlock();
}
}

注意事项:

一开始不明白为什么将frist置为NULL,后面在网上找了相关的资料了解到,如果不讲first置为Null会导致内存泄漏的问题,具体原因如下所示:

  • 如果不将first置为Null,线程A到达,队首元素还没到出列时间,设置线程A = leader
  • 线程B来了因为leader不为空 则会阻塞,后续线程一样。
  • 假如线程A阻塞完毕之后获取列首元素成功出列,这个时候列首元素应该被回收,但是它还被线程B C ...所持有一直不会被回收就导致了内存泄漏(gc一直无法回收frist这个对象)这个情况。

延迟队列DelayQueue take() 源码分析的更多相关文章

  1. 【RabbitMQ学习记录】- 消息队列存储机制源码分析

    本文来自 网易云社区 . RabbitMQ在金融系统,OpenStack内部组件通信和通信领域应用广泛,它部署简单,管理界面内容丰富使用十分方便.笔者最近在研究RabbitMQ部署运维和代码架构,本篇 ...

  2. java并发包——阻塞队列BlockingQueue及源码分析

    一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...

  3. JAVA并发(7)-并发队列PriorityBlockingQueue的源码分析

    本文讲PriorityBlockingQueue(优先阻塞队列) 1. 介绍 一个无界的具有优先级的阻塞队列,使用跟PriorityQueue相同的顺序规则,默认顺序是自然顺序(从小到大).若传入的对 ...

  4. ucos队列的实现--源码分析

    之前说到事件,讲了事件,信号量和互斥信号量,还有一个队列没说,今天说说队列. 队列是用在任务之间传送多个消息的时候,a任务发送消息,b任务发送消息,然后c任务可以依次去提取出b和a传递的消息,不会造成 ...

  5. 消息队列中间件 RocketMQ 源码分析 —— Message 存储

  6. 阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)

  7. jQuery.queue源码分析

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入.常用来表示先进先出的操作(FI ...

  8. Envoy 源码分析--event

    目录 Envoy 源码分析--event libevent Timer SignalEvent FileEvent RealTimeSystem 任务队列 延迟析构 dispacth_thread E ...

  9. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

随机推荐

  1. 指定vue的v-model的类型

    作为一个菜鸟,兼只会依葫芦画瓢的搬砖it狗,并没有系统学习过vue. 在最近项目中,发现了一个奇怪的问题: 使用vant-ui 的field <van-field v-model="f ...

  2. canvas入门,就是这个feel!

    钙素 Canvas 是在HTML5中新增的标签用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图.也就是说我们将通过JS完成画图而不是css. canva ...

  3. PostGIS 结合Openlayers以及Geoserver实现最短路径分析(二)

    前文讲述了怎么用ArcMap制作了测试数据,并导入了PostGIS,接下来我们需要结合PgRouting插件,对入库的数据再进行一下处理. 1.在pgAdmin中,执行下面的sql语句 --添加起点字 ...

  4. 原生JS scroll()、scrollTo()、scrollBy()

    scroll()  此方法接收两个参数,依次为X坐标和Y坐标:设置滚动条的偏移位置 scrollTo() 此方法和scroll()作用一样,都是设置滚动条的偏移位置. scrollBy() 此法发同样 ...

  5. day03_正则表达式

    1.数据分类 数据的分类 ​ 定义:数据以行为单位,每一个数据表示一个实体的信息.每一行数据的属性都是一样的. ​ 常见的结构化数据为关系型数据库存储数据. 半结构化数据 ​ 定义:结构化数据的另一种 ...

  6. Linux错误:Unable to locate package解决

    新买一个用于机器学习的实例,镜像系统Ubuntu.想安装一个上传.下载的包. 使用命令: sudo apt-get install lrzsz 结果一直报错: 解决方法: 使用命令: sudo apt ...

  7. python-面向对象之封装

    封装 面向对象三大特性: 继承 封装 多态 隐藏对象的属性和实现细节,仅对外提供公共访问方法 广义上的封装 : 把方法和变量都封装在类中 狭义上的封装 : 在类的外部干脆不能调用了 优点 将变化隔离 ...

  8. Windows系统中下载Earthdata数据

    总的来说,为四大步: 1.注册Earthdata用户. 注册时需注意的是,最好把所有需打勾的都勾上,在最后[注册]按钮前,弹出[人机验证]才能注册成功.如果注册不成功,除了检查用户名和密码是否符合要求 ...

  9. react之context

    context是什么 Props属性是由上到下单向传递的 context提供了在组件中共享此类值的方法 context使用 设计目的是共享哪些对于组件来说全局的数据 不要因为仅仅为了避免在几个层级下的 ...

  10. C# MVC 过滤器

    APS.NET MVC中(以下简称“MVC”)的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前前后后如果想再加一些额外的逻辑处理.这时候就用到了过滤器. MVC支持的过滤器 ...