【Java】Java Queue的简介
阻塞队列
阻塞队列有几个实现:
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- DelayQueue
- SynchronousQueue
- LinkedTransferQueue
- LinkedBlockingDeque
他们的共同父类是AbstractQueue。我们一起看ArrayBlockingQueue的实现。
ArrayBlockingQueue,数组、有界、出入队阻塞
数据存储
数据存储在数组中,用几个变量标记下一个获取或存储的元素:
/** The queued items */
final Object[] items; // 用数组存储元素
/** items index for next take, poll, peek or remove */
int takeIndex; // 返回元素的下标
/** items index for next put, offer, or add */
int putIndex; // 插入元素的下标
/** Number of elements in the queue */
int count; // 数量
阻塞逻辑
添加、删除元素需要使用ReentrantLock加锁,满队列、空队列情况的等待与唤醒使用各自的Condition:
public ArrayBlockingQueue(int capacity, boolean fair) {
...
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
插入元素,返回是否成功
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length) // 队列满,插入不成,返回false
return false;
else {
enqueue(e);
return true; // 插入成功,返回true
}
} finally {
lock.unlock();
}
}
插入元素,成功返回true,失败抛出异常
它调用offer方法,插入成功返回true,失败抛出异常:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
插入元素,队列满了则阻塞
public void put(E e) throws InterruptedException {
checkNotNull(e); // e为空抛出异常
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) // 当队列满了
notFull.await(); // notFull的Condition等待条件成熟
enqueue(e); // 条件成熟了才插入元素
} finally {
lock.unlock();
}
}
插入元素,队列满了则阻塞指定超时时间
主体逻辑与put(E e)一致,只是加了超时逻辑:
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout); // 将超时时间转换为Nano单位
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0) // 超时了,返回false
return false;
nanos = notFull.awaitNanos(nanos); // Condition等待指定时间
}
enqueue(e);
return true; // 超时时间内插入成功,返回true
} finally {
lock.unlock();
}
}
删除元素,返回是否删除成功
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i])) { // 遍历到要删除的元素,删除并返回true
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false; // 遍历完毕,没有找到,返回false
} finally {
lock.unlock();
}
}
删除元素,返回删除的元素,没匹配到返回null
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
删除元素,队列为空则阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) // 队列为空
notEmpty.await(); // 元素出队,队列元素为空,等待非空条件成熟
return dequeue();
} finally {
lock.unlock();
}
}
入队逻辑,入队成功后同时非空条件成熟:
private void enqueue(E x) { // 入队
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal(); // 元素入队后,通知非空条件已成熟
}
删除元素,队列为空阻塞指定超时时间
主体逻辑与take()一直,但有等待超时逻辑:
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout); // 转化为nano单位
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos); // 等待指定超时时间
}
return dequeue();
} finally {
lock.unlock();
}
}
LinkedBlockingQueue,链表、有界、出入队阻塞
存储结构
用链表作为存储,Node是链表的节点:
static class Node<E> {
E item; // 元素值
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node<E> next; // 下一节点
Node(E x) { item = x; } // 构造方法
}
PriorityBlockingQueue,无界,出队阻塞
出队阻塞
它是无界的,所以只有出队时队列无元素才会堵塞,依赖notEmpty的Condition:
/**
* Condition for blocking when empty
*/
private final Condition notEmpty;
优先级顺序
它的优先级依赖比较器:
/**
* The comparator, or null if priority queue uses elements'
* natural ordering.
*/
private transient Comparator<? super E> comparator;
DelayQueue,无界、出队阻塞、等待指定时间才能出队
数据存储
它的数据实现依赖于PriorityQueue,所以队列的元素需实现Comparable:
private final PriorityQueue<E> q = new PriorityQueue<E>();
出队
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek(); // 获取下一个即将的出队元素
if (first == null || first.getDelay(NANOSECONDS) > 0) // 如果无出队元素,或出队元素的时间未到
return null;
else
return q.poll(); // 实际的出队
} finally {
lock.unlock();
}
}
阻塞出队
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);
if (delay <= 0) // 时间已到,出队,跳出方法
return q.poll();
first = null; // don't retain ref while waiting // 等待期间取消引用
if (leader != null) # TODO,未理解透彻
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread; // 当前线程赋予leader
try {
available.awaitNanos(delay); // 等待剩余时间
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
SynchronousQueue,阻塞队列,不存储元素
依赖于TransferQueue和TransferStack
它可设置是否公平,分别依赖于TransferQueue和TransferStack,默认非公平
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
阻塞入队和出队
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
【Java】Java Queue的简介的更多相关文章
- java中Queue简介
Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 offer,add区别:一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝.这时新的 offer 方法 ...
- Lucene:基于Java的全文检索引擎简介
Lucene:基于Java的全文检索引擎简介 Lucene是一个基于Java的全文索引工具包. 基于Java的全文索引/检索引擎--Lucene Lucene不是一个完整的全文索引应用,而是是一个用J ...
- Java中的队列:java.util.Queue接口
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作. Queue接口与List.Set同一级别,都是继承了Collection接口.Linked ...
- java中使用队列:java.util.Queue
在java5中新添加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口.Queue使用时要尽量避免Collection的add()和 ...
- java集合--Queue用法
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列. 在队列这 ...
- Jconsole: JAVA 监视和管理控制台简介
Jconsole: JAVA 监视和管理控制台简介 JDK中除了提供大量的命令行之外,还提供两个功能强大的可视化工具:JConsole和VisualVM. 之前对java的调试一直停留在 右键-> ...
- java.util.Queue用法
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列. 在队列这 ...
- java队列——queue详细分析
Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 Queue接口与List.Set同一级别,都是继承了Collection接口.LinkedList实现了Deque接 口. Q ...
- java assert的用法简介【转】
assert的基本用法 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制,如C,C++和Eiffel等,但是支持的形式不尽相同,有的是通过语言本身.有的是通过库 ...
- Java的MVC模式简介
Java的MVC模式简介 MVC(Model View Control)模型-视图-控制器 首先我们需要知道MVC模式并不是javaweb项目中独有的,MVC是一种软件工程中的一种软件架构模式,把软件 ...
随机推荐
- jstl标签比较格式化后的时间
c:set 里面不支持任何标签,这样写不好讲格式化的值放到bdateVar里面 <c:set var="bdateVar" value="<fmt:forma ...
- 004.Kickstart部署之FTP架构
一 准备 1.1 完整架构:Kickstart+DHCP+VSFTP+TFTP+PXE 1.2 组件应用 Kickstart服务端IP:172.24.8.12 DHCP:提供客户端IP,网关,镜像路径 ...
- Flutter中打造多行列列表GridView组件的使用
GridView组件.一个可滚动的二维空间数组. 在使用无限加载滚动列表的时候,最先使用的还是ListView组件.但若是要一行显示2列或者更多列的滚动列表,GridView组件更为方便.如下 在向服 ...
- 不一样的go语言-构建系统与构件系统
前言 代码的最后一步是构建成计算机可识别的二进制数据,然后才得以在计算机上运行.如果你曾经写过有点规模(至少数十个以上独立的源文件,且需要依赖第三方包)C语言项目,必定对C语言项目的构建过程印象深 ...
- go语言学习-基础知识
go程序的基本结构 一个可以最简单的可运行的go程序需要满足下面两个条件: 有一个main()函数 main()函数在main包中 例如: 在go语言中的 hello world 程序如下: // m ...
- idea颜色主题
作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com E-mail: 313134555 @qq.com IDEA 主题样式 === 这个垂直线的 颜 ...
- bzoj3545: [ONTAK2010]Peaks 重构树 主席树
题目链接 bzoj3545: [ONTAK2010]Peaks 题解 套路重构树上主席树 代码 #include<cstdio> #include<algorithm> #de ...
- [CC-LONCYC]Lonely Cycles
[CC-LONCYC]Lonely Cycles 题目大意: \(T(T\le1000)\)组数据. 给定一张简单图(不含重边与自环),图中有\(n(n\le2\times10^5)\)个节点和\(m ...
- RouterOS双线进行IP分流上网
环境: 1.第一条:电信静态IP,一级路由分配的IP:第二条:移动光纤 2.通过指定某些IP走电信,某些走移动 注意: 1.当有多条线路进行NAT伪装时,Out. Interface这个必须选择具体的 ...
- CentOS安装mysql*.rpm提示conflicts with file from package的解决办法
看到“conflicts”,是产生冲突了,文件“/usr/share/mysql/charsets/*”需要MySQL-server-5.6.19-1.linux_glibc2.5.x86_64版本的 ...