Java集合–阻塞队列(ArrayBlockingQueue)

1 ArrayBlockingQueue

ArrayBlockingQueue是一个阻塞队列,底层使用数组结构实现,按照先进先出(FIFO)的原则对元素进行排序。

ArrayBlockingQueue是一个线程安全的集合,通过ReentrantLock锁来实现,在并发情况下可以保证数据的一致性。

此外,ArrayBlockingQueue的容量是有限的,数组的大小在初始化时就固定了,不会随着队列元素的增加而出现扩容的情况,也就是说ArrayBlockingQueue是一个“有界缓存区”。

在下面图片中,以数组形式展示了一个ArrayBlockingQueue:

当向队列插入元素时,首先会插入到数组的0角标处,再有新元素进来时,依次类推,角标1、角标2、角标3。

整个item[]就是一个队伍,我们用时间来排序,展示入队场景。

而当有元素出队时,先移除角标为0的元素,与入队一样,依次类推,移除角标1、角标2…上的元素。

这也形成了“先进先出”。

接下来,我们来看看ArrayBlockingQueue的源码实现!

  • 构造方法

在多线程中,默认不保证线程公平的访问队列。

什么叫做公平访问队列?我们都知道,在ArrayBlockingQueue中为了保证数据的安全,使用了ReentrantLock锁。由于锁的引入,导致了线程之间的竞争。当有一个线程获取到锁时,其余线程处于等待状态。当锁被释放时,所有等待线程为夺锁而竞争。

而所谓的公平访问,就是等待的线程在获取锁而竞争时,按照等待的先后顺序进行获取操作,先等待的先获取,后等待的后获取。

而非公平访问,就是在获取时候,无论是先等待还是后等待的线程,均有可能获取到锁。

在ArrayBlockingQueue中,由于公平锁会降低队列的性能,因而使用非公平锁(默认)。

是否公平,根据ReentrantLock对象来实现—ReentrantLock lock = new ReentrantLock(false),具体看下构造便可得知。

public class ArrayBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {//队列实现:数组final Object[] items;//当读取元素时数组的下标(下一个被添加元素的索引)int takeIndex;//添加元素时数组的下标 (下一个被取出元素的索引)int putIndex;//队列中元素个数:int count;//锁:final ReentrantLock lock;//控制take()操作时是否让线程等待private final Condition notEmpty;//控制put()操作时是否让线程等待private final Condition notFull;//初始化队列容量构造:public ArrayBlockingQueue(int capacity) {this(capacity, false);}//带初始容量大小和公平锁队列(公平锁通过ReentrantLock实现):public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();this.items = new Object[capacity];lock = new ReentrantLock(fair);notEmpty = lock.newCondition();notFull =  lock.newCondition();}
}
  • 插入元素

在ArrayBlockingQueue中,提供了两种不同形式的元素插入–阻塞式和非阻塞式。

对于阻塞式插入来说,当队列中的元素已满时,则会将此线程停止,让其处于等待状态,直到队列中有空余位置产生。

//向队列尾部添加元素,如果队列满了,则线程等待
public void put(E e) throws InterruptedException {//不能插入非空元素,会抛出异常checkNotNull(e);//上锁,保证数据安全final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {//队列中元素 == 数组长度(队列满了),则线程等待while (count == items.length)notFull.await();//添加队列元素insert(e);} finally {//插入完成,释放锁lock.unlock();}
}

而对于非阻塞式来说,当队列中的元素已满时,并不会阻塞此线程的操作,而是让其返回又或者是抛出异常。

//向队列尾部添加元素,队列满了返回false
public boolean offer(E e) {//不能插入非空元素,会抛出异常checkNotNull(e);//上锁,保证数据安全final ReentrantLock lock = this.lock;lock.lock();try {//队列中元素 == 数组长度(队列满了),则返回falseif (count == items.length)return false;else {//添加队列元素insert(e);return true;}} finally {//插入完成,释放锁lock.unlock();}
}

上面的offer(E e)并不会阻塞线程的执行,但是如果想让阻塞和非阻塞相结合的话,需要怎么处理?

ArrayBlockingQueue为我们提供了折中的方法–offer(E e, long timeout, TimeUnit unit);

向队列尾部添加元素,可以设置线程等待时间,如果超过指定时间队列还是满的,则返回false;

public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {//不能插入非空元素,会抛出异常checkNotNull(e);//转换成超时时间阀值:long nanos = unit.toNanos(timeout);//上锁,保证数据安全final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {//对队列是否元素满了,做判断。while (count == items.length) {//如果队列是满的,则每次遍历都去递减一次nanos的值if (nanos <= 0)return false;nanos = notFull.awaitNanos(nanos);}//添加队列元素insert(e);return true;} finally {//插入完成,释放锁lock.unlock();}
}

以上添加方法,都是通过返回false/true来实现的,而在ArrayBlockingQueue中,还提供了集合最原始的插入方法–add(E e)。

该方法在插入时候,如果队列中的元素满了,则会抛出异常。如果插入成功,则返回true。

在add(E e)中,使用父类的add(E e),实际上其底层也是调用的offer(E e)方法。

//向队列尾部添加元素,队列满了抛出异常;
public boolean add(E e) {return super.add(e);
}

ArrayBlockingQueue中,最底层的插入方法,上面的各种实现,都是基于insert(E x)来实现的。由于insert(E x)是用private来修饰的,所以我们不能直接对其进行调用。

//插入元素到队尾,调整putIndex,唤起等待的获取线程
private void insert(E x) {//向数组中插入元素items[putIndex] = x;//设置下一个被取出元素的索引putIndex = inc(putIndex);//增加队列元素个数:++count;//唤醒notEmpty上的等待线程notEmpty.signal();
}
  • 获取元素

    //获取队列头部元素,如果队列为空,则返回null.不为空。
    // 则返回队列头部,并从队列中删除。
    public E poll() {
    ​ final ReentrantLock lock = this.lock;
    ​ lock.lock();
    ​ try {
    ​ return (count == 0) ? null : extract();
    ​ } finally {
    ​ lock.unlock();
    ​ }
    }

//返回队列的头部元素,并从队列中删除。如果队列为空,则等待public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {//如果队列为空,则进行等待while (count == 0)notEmpty.await();//获取头部元素:return extract();} finally {lock.unlock();}}//获取队列头部元素,如果队列为空,则设置线程等待时间,超过指定时间,还为空,则返回null。public E poll(long timeout, TimeUnit unit) throws InterruptedException {long nanos = unit.toNanos(timeout);final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0) {if (nanos <= 0)return null;nanos = notEmpty.awaitNanos(nanos);}return extract();} finally {lock.unlock();}}

以上就是关于ArrayBlockingQueue的全部内容!下面,我们继续说说LinkedBlockingQueue

Java集合--阻塞队列(ArrayBlockingQueue)相关推荐

  1. Java集合--阻塞队列(LinkedBlockingQueue)

    Java集合–阻塞队列(LinkedBlockingQueue) 1. LinkedBlockingQueue LinkedBlockingQueue是一个使用链表实现的阻塞队列,支持多线程并发操作, ...

  2. JAVA可阻塞队列-ArrayBlockingQueue

    在前面的的文章,写了一个带有缓冲区的队列,是用JAVA的Lock下的Condition实现的,但是JAVA类中提供了这项功能,就是ArrayBlockingQueue, ArrayBlockingQu ...

  3. Java阻塞队列ArrayBlockingQueue和LinkedBlockingQueue实现原理分析

    转载自  Java阻塞队列ArrayBlockingQueue和LinkedBlockingQueue实现原理分析 Java中的阻塞队列接口BlockingQueue继承自Queue接口. Block ...

  4. java 队列_百战程序员:Java并发阻塞队列

    阻塞队列 (BlockingQueue)是Java util.concurrent包下重要的数据结构,BlockingQueue提供了线程安全的队列访问方式:当阻塞队列进行插入数据时,如果队列已满,线 ...

  5. JAVA中阻塞队列的类别和区别(转载)

    这篇文章将介绍什么是阻塞队列,以及Java中阻塞队列的4种处理方式,并介绍Java 7中提供的7种阻塞队列,最后分析阻塞队列的一种实现方式. 阻塞队列(BlockingQueue)是一个支持两个附加操 ...

  6. Java 实现阻塞队列

    Java 实现阻塞队列 @FunctionalInterface interface RejectPolicy<T> {void reject(BlockingQueue<T> ...

  7. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

  8. Java多线程-新特征-阻塞队列ArrayBlockingQueue

    阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素 ...

  9. 阻塞队列 java 源码_Java源码解析阻塞队列ArrayBlockingQueue常用方法

    本文基于jdk1.8进行分析 首先看一下ArrayBlockingQueue的成员变量.如下图.最主要的成员变量是items,它是一个Object类型的数组用于保存阻塞队列中的元素.其次是takeIn ...

最新文章

  1. java按升序冒泡排序_Java实现冒泡排序算法
  2. vulnhub_内网渗透测试的记录——网络安全
  3. 微服务:实战从传统项目平滑过渡 - 笔记
  4. linux tcp 创建,Linux下tcp服务器创建的步骤
  5. python全局变量的声明和使用_python自学篇(第三章:函数)
  6. Matlab求解线性规划
  7. WinCE开发流媒体播放器--MPEG4
  8. 豆瓣已玩烂,来爬点有逼格的 ——IMDB 电影提升你的品位
  9. 用 HBuilder X 编辑 Markdown 文档,如何自定义表格列宽
  10. 程序员如何提高影响力2.0
  11. 什么是Bounding Box、anchor box?
  12. 在校园网的环境下用树莓派搭建私人云
  13. Win10如何启用Administrator账户
  14. 传统研发团队的敏捷转型实践之路
  15. Oracle 12c统一审计
  16. 当Python遇到分形数学魔法 --> 树叶
  17. 黄金期货对比现货黄金有哪些优势
  18. web漏洞扫描器-Burpsuite 常规测试
  19. xss-labs通关大详解
  20. 爬取百大弹幕,大家还是喜欢上罗老师的课!

热门文章

  1. 基站、WiFi、IP定位原理介绍与区别
  2. lopa分析_什么是LOPA分析?
  3. 【Practical】CSDN图片去除水印
  4. 类型多样的终结者游戏成套模型素材,速来收藏
  5. 解决浏览器驱动和浏览器版本不匹配的报错:This version of ChromeDriver only supports Chrome version 97
  6. Android 8.0 蓝牙唤醒 Ble 锁屏 保活 后台 持续扫描 进程拉活 自动唤醒
  7. 云服务器无法访问解决办法
  8. win10打字不显示选字框
  9. JavaScript - 将 Allegro 坐标文件转为嘉立创坐标文件(CSV 格式)的工具
  10. Shell发送邮件+附件