java加载c库阻塞_【死磕Java並發】-----J.U.C之阻塞隊列:DelayQueue
DelayQueue是一個支持延時獲取元素的無界阻塞隊列。里面的元素全部都是“可延期”的元素,列頭的元素是最先“到期”的元素,如果隊列里面沒有元素到期,是不能從列頭獲取元素的,哪怕有元素也不行。也就是說只有在延遲期到時才能夠從隊列中取元素。
DelayQueue主要用於兩個方面:
- 緩存:清掉緩存中超時的緩存數據
- 任務超時處理
DelayQueue
DelayQueue實現的關鍵主要有如下幾個:
可重入鎖ReentrantLock
用於阻塞和通知的Condition對象
根據Delay時間排序的優先級隊列:PriorityQueue
用於優化阻塞通知的線程元素leader
ReentrantLock、Condition這兩個對象就不需要闡述了,他是實現整個BlockingQueue的核心。PriorityQueue是一個支持優先級線程排序的隊列(參考【死磕Java並發】—–J.U.C之阻塞隊列:PriorityBlockingQueue),leader后面闡述。這里我們先來了解Delay,他是實現延時操作的關鍵。
Delayed
Delayed接口是用來標記那些應該在給定延遲時間之后執行的對象,它定義了一個long getDelay(TimeUnit unit)方法,該方法返回與此對象相關的的剩余時間。同時實現該接口的對象必須定義一個compareTo 方法,該方法提供與此接口的 getDelay 方法一致的排序。
public interface Delayed extends Comparable {
long getDelay(TimeUnit unit);
}
如何使用該接口呢?上面說的非常清楚了,實現該接口的getDelay()方法,同時定義compareTo()方法即可。
內部結構
先看DelayQueue的定義:
public class DelayQueue extends AbstractQueue
implements BlockingQueue {
/** 可重入鎖 */
private final transient ReentrantLock lock = new ReentrantLock();
/** 支持優先級的BlockingQueue */
private final PriorityQueue q = new PriorityQueue();
/** 用於優化阻塞 */
private Thread leader = null;
/** Condition */
private final Condition available = lock.newCondition();
/**
* 省略很多代碼
*/
}
看了DelayQueue的內部結構就對上面幾個關鍵點一目了然了,但是這里有一點需要注意,DelayQueue的元素都必須繼承Delayed接口。同時也可以從這里初步理清楚DelayQueue內部實現的機制了:以支持優先級無界隊列的PriorityQueue作為一個容器,容器里面的元素都應該實現Delayed接口,在每次往優先級隊列中添加元素時以元素的過期時間作為排序條件,最先過期的元素放在優先級最高。
offer()
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 向 PriorityQueue中插入元素
q.offer(e);
// 如果當前元素的對首元素(優先級最高),leader設置為空,喚醒所有等待線程
if (q.peek() == e) {
leader = null;
available.signal();
}
// 無界隊列,永遠返回true
return true;
} finally {
lock.unlock();
}
}
offer(E e)就是往PriorityQueue中添加元素,具體可以參考(【死磕Java並發】—–J.U.C之阻塞隊列:PriorityBlockingQueue)。整個過程還是比較簡單,但是在判斷當前元素是否為對首元素,如果是的話則設置leader=null,這是非常關鍵的一個步驟,后面闡述。
take()
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
// 對首元素
E first = q.peek();
// 對首為空,阻塞,等待off()操作喚醒
if (first == null)
available.await();
else {
// 獲取對首元素的超時時間
long delay = first.getDelay(NANOSECONDS);
// <=0 表示已過期,出對,return
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
// leader != null 證明有其他線程在操作,阻塞
if (leader != null)
available.await();
else {
// 否則將leader 設置為當前線程,獨占
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 超時阻塞
available.awaitNanos(delay);
} finally {
// 釋放leader
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 喚醒阻塞線程
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
首先是獲取對首元素,如果對首元素的延時時間 delay <= 0 ,則可以出對了,直接return即可。否則設置first = null,這里設置為null的主要目的是為了避免內存泄漏。如果 leader != null 則表示當前有線程占用,則阻塞,否則設置leader為當前線程,然后調用awaitNanos()方法超時等待。
first = null
這里為什么如果不設置first = null,則會引起內存泄漏呢?線程A到達,列首元素沒有到期,設置leader = 線程A,這是線程B來了因為leader != null,則會阻塞,線程C一樣。假如線程阻塞完畢了,獲取列首元素成功,出列。這個時候列首元素應該會被回收掉,但是問題是它還被線程B、線程C持有着,所以不會回收,這里只有兩個線程,如果有線程D、線程E…呢?這樣會無限期的不能回收,就會造成內存泄漏。
這個入隊、出對過程和其他的阻塞隊列沒有很大區別,無非是在出對的時候增加了一個到期時間的判斷。同時通過leader來減少不必要阻塞。
歡迎掃一掃我的公眾號關注 — 及時得到博客訂閱哦!
–— Java成神之路: 488391811(一起走向Java成神) –—
java加载c库阻塞_【死磕Java並發】-----J.U.C之阻塞隊列:DelayQueue相关推荐
- java 原子类能做什么_死磕 java原子类之终结篇(面试题)
概览 原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换. 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割 ...
- java 同步锁_死磕 java同步系列之自己动手写一个锁Lock
问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...
- java任务流程_死磕 java线程系列之线程池深入解析——普通任务执行流程
(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 前面我们一起学习了Java中 ...
- java加载并运行虚拟机_《深入理解Java虚拟机》- Java虚拟机是如何加载Java类的?...
Java虚拟机是如何加载Java类的? 这个问题也就是面试常问到的Java类加载机制.在年初面试百战之后,菜鸟喜鹊也是能把这流程倒背如流啊!但是,也只是字面上的背诵,根本就是像上学时背书考试一样. ...
- java 加载class文件路径_动手实现MVC: 1. Java 扫描并加载包路径下class文件
背景 用过spring框架之后,有个指定扫描包路径,然后自动实例化一些bean,这个过程还是比较有意思的,抽象一下,即下面三个点 如何扫描包路径下所有的class文件 如何扫描jar包中对应包路径下所 ...
- java 加载dll后打包_让Jacob从当前路径读取dll文件及相关打包方法
让Jacob从当前路径读取dll文件及相关打包方法 独立观察员2013.08.12 Jacob LibraryLoader.class修改版代码 功能:让jacob可在当前路径下的dll文件夹内读取 ...
- java 手编线程池_死磕 java线程系列之自己动手写一个线程池
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...
- 2019死磕java面试题_死磕 java同步系列之开篇
简介 同步系列,这是彤哥想了好久的名字,本来是准备写锁相关的内容,但是java中的CountDownLatch.Semaphore.CyclicBarrier这些类又不属于锁,它们和锁又有很多共同点, ...
- java ee是什么_死磕 java集合之HashSet源码分析
问题 (1)集合(Collection)和集合(Set)有什么区别? (2)HashSet怎么保证添加元素不重复? (3)HashSet是否允许null元素? (4)HashSet是有序的吗? (5) ...
最新文章
- 计算机网络技术问题解决,计算机网络故障常见问题汇总,掌握了这些,你离女神又会更进一步...
- 系统分析的几个好工具
- anaconda安装好tensorflow后,无法在jupyter notebook上使用的解决方法
- pytorch之深度学习
- cad文件格式(dwg、dxf、dwf、dws等)转其他格式(svg、,tiff、jpej、png、xml、pdf等)的四种方式(java)
- Linux CentOS 7修改主机名称
- CodeForces128A - Statues 解题报告
- python小于_删除python中小于某个值的行
- 阿里巴巴矢量图库开源http://www.iconfont.cn/collections/detail?cid=29
- 【深度】新派LaaS协议Elephant:重振DeFi赛道发展的关键
- Vim 增加man快捷方式
- 【电脑配置知识】显卡 GPU
- nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件,很全
- 微信小程序如何进行反编译详细教程
- 关于数字出版物的版权
- HashMap是线程安全的吗?有什么线程安全的方法
- 计算机图形学算法总结
- Classification and inference with machine learning
- 银河系中心黑洞的第一张照片,本文带你了解发现的过程
- C语言学习(十一)之字符输入/输出
热门文章
- CSS3圆圈动画放大缩小循环动画效果
- CSS篇 《图解CSS3》笔记 Flex
- [转] GIS算法源码集合
- 删除不同粒度的事实表记录中重复的度量值数据的SQL语句
- 5.什么是二叉查找树?原理
- 天马行空W:在C++中调用DLL中的函数
- 野火STM32F103教学视频完整目录(配合霸道-指南者开发板)
- 接上一篇Ansible和celery的结合,在celery的tasks.py文件里为了实现并发不阻塞的需求,用到了多进程
- linux通过管道的进程通信,linux 线程或进程之间通过管道通信(pipe)
- docker pip 换源_Docker 部署 jupyterlab 3.0.3