阻塞队列

阻塞队列有几个实现:

  • 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的简介的更多相关文章

  1. java中Queue简介

    Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 offer,add区别:一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝.这时新的 offer 方法 ...

  2. Lucene:基于Java的全文检索引擎简介

    Lucene:基于Java的全文检索引擎简介 Lucene是一个基于Java的全文索引工具包. 基于Java的全文索引/检索引擎--Lucene Lucene不是一个完整的全文索引应用,而是是一个用J ...

  3. Java中的队列:java.util.Queue接口

    队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作. Queue接口与List.Set同一级别,都是继承了Collection接口.Linked ...

  4. java中使用队列:java.util.Queue

    在java5中新添加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口.Queue使用时要尽量避免Collection的add()和 ...

  5. java集合--Queue用法

    队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列. 在队列这 ...

  6. Jconsole: JAVA 监视和管理控制台简介

    Jconsole: JAVA 监视和管理控制台简介 JDK中除了提供大量的命令行之外,还提供两个功能强大的可视化工具:JConsole和VisualVM. 之前对java的调试一直停留在 右键-> ...

  7. java.util.Queue用法

    队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列. 在队列这 ...

  8. java队列——queue详细分析

    Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 Queue接口与List.Set同一级别,都是继承了Collection接口.LinkedList实现了Deque接 口.   Q ...

  9. java assert的用法简介【转】

    assert的基本用法 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制,如C,C++和Eiffel等,但是支持的形式不尽相同,有的是通过语言本身.有的是通过库 ...

  10. Java的MVC模式简介

    Java的MVC模式简介 MVC(Model View Control)模型-视图-控制器 首先我们需要知道MVC模式并不是javaweb项目中独有的,MVC是一种软件工程中的一种软件架构模式,把软件 ...

随机推荐

  1. C语言 —— sprintf()函数

    sprintf() 函数 例如: #include<stdio.h> int main() { //注意s一定要先初始化! *sizeof(char)); //或char s[40]; s ...

  2. socket.io的用户认证

    一直专心于写代码,遇到问题便看别人的博客来解决问题,突然感觉自己也应该写点东西帮助别人来解决问题.废话不多说了,直接切入正题~ 最近在写一个聊天室来学习node和socket相关知识.遇到的问题描述: ...

  3. 跳过从Win7/8升级,直接格式化全新安装 Windows 10 并自动永久激活系统的方法教程

    跳过升级,直接激活全新 Win10 的方法步骤教程: 下载 Windows 10 系统的 ISO 镜像 在你当前的 Win7 / Win 8 / 8.1 系统中,使用 DaemonTools 或右键选 ...

  4. AGC001 E - BBQ Hard 组合数学

    题目链接 AGC001 E - BBQ Hard 题解 考虑\(C(n+m,n)\)的组合意义 从\((0,0)\)走到\((n,m)\)的方案数 从\((x,y)\)走到\((x+n,y+m)\)的 ...

  5. HDU.1536.S-Nim(博弈论 Nim)

    题目链接 \(Description\) 给定一个集合S,每次只能拿S中某个元素个数的石子.每组数据有多组询问,询问给出m堆石子个数,问先手是否必胜.有多组数据. 1. 首先对操作数组排个序,再预处理 ...

  6. 潭州课堂25班:Ph201805201 第八课:函数基础和函数参数 (课堂笔记)

    1, 函数定义 def fun(): print('测试函数') fun() #调用函数 return 运行函数返回值 def fun(): name = [1,3,4,5] return name[ ...

  7. bzoj 4084 双旋转字符串

    给两个集合A,B,找满足要求的(a,b)的对数,可以计算对于a,哪些b成立. 还有就是字符串hash的使用,感觉平时用字符串hash太少了. /**************************** ...

  8. iOS CrashLog Analysis

    链接: iOS友盟崩溃日志定位代码 [友盟统计报表解读]之错误分析iOS版 DYSMTool Download Address 利用友盟和dsym分析发布app用户错误 iOS -- 友盟工具进行Cr ...

  9. caffe solver 配置详解

    caffe solver通过协调网络前向推理和反向梯度传播来进行模型优化,并通过权重参数更新来改善网络损失求解最优算法,而solver学习的任务被划分为:监督优化和参数更新,生成损失并计算梯度.caf ...

  10. webservice-整理

    webservice-整理 RPC与WebService的区别:https://blog.csdn.net/defonds/article/details/71641634 http://www.di ...