阻塞队列

阻塞队列有几个实现:

  • 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. cmake使用笔记

    目录 cmake使用笔记 基本使用方法 相较于makefile的优点 常用语法 cmake_minimum_required project PROJECT_SOURCE_DIR set includ ...

  2. 【Ray Tracing in One Weekend 超详解】 光线追踪1-5

    一天一篇,今天来学习第7章 (散射)漫反射材质 Chapter7: Diffuse Materials Preface 从这一章开始,我们将通过光线追踪制作一些逼真的材质. 我们将从漫射(磨砂)材料开 ...

  3. vim编辑器基本操作

    命令模式: 按(i)键进入编辑模式,将在光标前面插入: 按(I)键进入编辑模式,将在光标行首插入: 按(a)进入编辑模式,在光标后面插入: 按(A)键进入编辑模式,将在光标行末插入: 按(o)进入编辑 ...

  4. Luogu2570 [ZJOI2010]贪吃的老鼠 ---- 网络流

    Luogu2570  [ZJOI2010]贪吃的老鼠 题面描述 https://www.luogu.org/problemnew/show/P2570 然后题意大概就是m只老鼠,然后吃n个奶酪,已知 ...

  5. web开发 入门

    插件 ,索引文件,js目录,视图目录,资产目录,css目录,数据目录,font-awesome-4.7.0目录,图像目录. 引导程序 框架.字体.layer,mockjs.paging分页.树网格.t ...

  6. 安卓工作室 android studio 汉化后,报错。 设置界面打不开。Can't find resource for bundle java.util.PropertyResourceBundle, key emmet.bem.class.name.element.separator.label

    安卓工作室 android studio 汉化后,报错. 设置界面打不开. Android studio has been sinified and reported wrong.The setup ...

  7. JS 显示周 几和 月 日

    function getMyDay(date){ var week; ; var day = date.getDate(); ) week="周日" ) week="周一 ...

  8. 支付宝支付集成过程中如何生成商户订单号(out_trade_no)

    out_trade_no是指商户网站唯一订单号,在商户端唯一,每个商户订单号会对应一个支付宝订单号 ,此订单号由珊瑚自己生成,商户订单号要求64个字符以内.可包含字母.数字.下划线:需保证在商户端不重 ...

  9. 静态代理、JDK动态代理和CGLib动态代理之前的区别

    昨天看了一天的代理方面的知识,刚开始看的时候没看出什么花头来,感觉不实用.一大堆的东西,还不如直接new出来,然后调用方法.后来仔细研究了一下AOP(面向切面)的思想,才发现代理的用处实在太大了.现在 ...

  10. spring-boot 速成(1) helloworld

    一.mac上安装 $ brew tap pivotal/tap $ brew install springboot 安装成功后,可在终端查看命令行 ➜  ~ spring --versionSprin ...