jdk11源码--ArrayBlockingQueue源码分析

简介: jdk11 ArrayBlockingQueue源码分析

概述

上一篇文章jdk11源码--ReentrantLock之Condition源码分析中分析了ReentrantLock和Condition的源码,那么接下来看一下Condition在JDK中的具体应用。

ArrayBlockingQueue底层就是使用Condition来实现的。

BlockingQueue

BlockingQueue 阻塞队列,该类是一个接口,平时我们熟知的ArrayBlockingQueue,LinkedBlockingQueue等都是该接口的实现。
BlockingQueue 之所以说是阻塞的,是因为他可以在队列为空的时候,获取元素的线程会阻塞,直到有新的元素添加进来。当队列满时,添加元素的线程会阻塞,直到有线程从队列中取走了元素。这也是注明的==生产者消费者==问题。
当有面试官问你==生产者消费者==问题时,直接将ArrayBlockingQueue源码分析讲一下也是可以的。

看一下BlockingQueue的类图,BlockingQueue是继承自Queue,而queue继承自collection。
在这里插入图片描述

BlockingQueue 对插入、删除、获取原色的操作提供了四种不同的方法用于不同的场景中使用,这些方法总结在下表中:

抛出异常Throws exception Special value ==Blocks== Times out
插入数据Insert add(e) //队列满时抛异常 offer(e) //队列满返回false,不阻塞 ==put(e)== //队列满时阻塞,直到队列未满时再插入 offer(e, time, unit) //指定直接内可以插入返回true,指定时间内不能插入,返回false
获取数据Remove remove()//队列为空,抛异常 poll()//队列为空返回null,不阻塞 ==take()== //当队列为空时会阻塞,一直等到队列不为空时再返回队首值 poll(time, unit) //在指定时间内,队列都是空,则返回null,否则返回对首的值
Examine element() peek()

本文我们重点关注 put和take方法,因为这两个方法时阻塞的。

ArrayBlockingQueue

ArrayBlockingQueue是BlockingQueue的一个实现。他是FIFO先进先出队列。
ArrayBlockingQueue类图:
在这里插入图片描述
重要属性:

/** 队列集合,数组存储!! */
final Object[] items;
/** take, poll, peek or remove 方法获取元素的下标位置 */
int takeIndex;
/** put, offer, or add 方法添加元素的下标位置 */
int putIndex;

/** 队列中的元素数量 */
int count;

//并发控制
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;

items是使用数组存储的,这也是ArrayBlockingQueue名称的由来。

并发控制使用经典的双Condition 算法,上面定义了两个Condition ,一个notEmpty,一个notFull。下面来逐行源码具体分析一下。

ArrayBlockingQueue构造方法

//capacity: 数组初始容量
public ArrayBlockingQueue(int capacity) {
    this(capacity, false);
}
//fair:是否是公平锁
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();
}

在构造函数中会指定初始容量以及锁的类型,默认是非公平锁。
数组items一旦确定下来,后续就不会再更改大小。

put

put方法添加元素到队尾,当队列满时阻塞。

public void put(E e) throws InterruptedException {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;//获取锁lock
    lock.lockInterruptibly();//加锁,响应中断
    try {
        while (count == items.length)
            notFull.await();//如果items满了,那么notFull阻塞
        enqueue(e);//将元素添加到队列末尾
    } finally {
        lock.unlock();
    }
}

//将元素添加到队列末尾, ++putIndex,count++,
//该方法只允许在lock加锁后操作
private void enqueue(E e) {
    final Object[] items = this.items;
    items[putIndex] = e;
    if (++putIndex == items.length) putIndex = 0;
    count++;
    notEmpty.signal();//唤醒阻塞的获取元素的线程
}

整个过程还是比较简单的,首先加锁,注意这里的锁允许中断返回。然后,如果队列满了(count == items.length),那么就无法继续添加元素了,添加元素的线程就需要await等待(notFull.await();),该线程添加到notFull的condition队列上,直到被take方法唤醒(后面会讲)后,继续添加元素到队尾(enqueue(e);)。
enqueue方法中,添加元素到putIndex 的位置,然后对putIndex 加1操作,由于是数组,所以这里进行了越界处理,越界后从0开始继续计算。count元素的数量进行加1操作,同时唤醒notEmpty condition队列上阻塞的线程。
读者可以思考一下这里为什么没有进行这个校验:putIndex 在加一以后是否会与现有队列中已经存在的元素重合而覆盖掉现有元素?答案下一节揭晓。

take

take:从队首获取元素。

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();//加锁,响应中断
    try {
        while (count == 0)
            notEmpty.await();//如果队列中空,那么当前线程需要添加到notEmpty的condition队列中阻塞,直到有新的元素添加进来
        return dequeue();
    } finally {
        lock.unlock();
    }
}

//从对首获取元素,
//该方法只允许在lock加锁后操作
private E dequeue() {
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E e = (E) items[takeIndex];
    items[takeIndex] = null;
    if (++takeIndex == items.length) takeIndex = 0;//设置takeIndex 下一次应该获取的位置
    count--;//队列总数量-1
    if (itrs != null)
        itrs.elementDequeued();//迭代器相关
    notFull.signal();//唤醒notFull condition队列上的线程
    return e;
}

take过程也很简单,先看一下队列是否为空,如果为空,则无法获取元素,将当前线程添加到notEmpty的condition队列。否则从takeIndex 的位置读取元素并且设置takeIndex ,count 的值。

上一节提到了这个问题:
putIndex 在加一以后是否会与现有队列中已经存在的元素重合而覆盖掉现有元素?
其实也很简单,enqueue和dequeue都是需要加锁以后才调用的,所以是线程安全的,count可以准确表示队列中的有效长度,takeIndex 和putIndex 也都没有并发问题,他们每次添加或者读取时都会判断count的值,来确认队列是否满或者空。如此一来,当然不会出现添加过多覆盖现有元素的情况。

总结

首先回顾一下上一篇jdk11源码--ReentrantLock之Condition源码分析中画的condition结构图吗,以及condition与ReentrantLock之间的关系。
然后再次基础上,画一下ArrayBlockingQueue中的condition关系图:
在这里插入图片描述
总体来讲,ArrayBlockingQueue包含一个ReentrantLock和两个condition:notEmpty和notFull。
put可take操作都需要加锁,都是线程安全的。
当队列满时,put操作需阻塞等待,当前线程添加到notFull的condition队列中;添加成功时,需唤醒notEmpty队列中的线程。
当队列空时,take操作需阻塞等待,当前线程添加到notEmpty的condition队列中;获取成功时,需唤醒notFull队列中的线程。

相关文章
|
7月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
10月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
452 3
|
11月前
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
344 6
家政系统源码,java版本
|
11月前
|
供应链 JavaScript 前端开发
Java基于SaaS模式多租户ERP系统源码
ERP,全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统,它通过信息技术手段,将企业的各个业务流程和资源管理进行整合,以提高企业的运营效率和管理水平,它是一种先进的企业管理理念和信息化管理系统。 适用于小微企业的 SaaS模式多租户ERP管理系统, 采用最新的技术栈开发, 让企业简单上云。专注于小微企业的应用需求,如企业基本的进销存、询价,报价, 采购、销售、MRP生产制造、品质管理、仓库库存管理、财务应收付款, OA办公单据、CRM等。
690 23
|
12月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
699 7
|
12月前
|
Java 关系型数据库 MySQL
Java汽车租赁系统源码(含数据库脚本)
Java汽车租赁系统源码(含数据库脚本)
427 4
|
12月前
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
12月前
|
Java
【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
本文深入解析了ConcurrentHashMap的实现原理,涵盖JDK 7与JDK 8的区别、静态代码块、构造方法、put/get/remove核心方法等。JDK 8通过Node数组+链表/红黑树结构优化并发性能,采用CAS和synchronized实现高效锁机制。文章还详细讲解了hash计算、表初始化、扩容协助及计数更新等关键环节,帮助读者全面掌握ConcurrentHashMap的工作机制。
291 6
【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
|
12月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
391 5
|
12月前
|
Java
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门
前言 有了前文对简单实用的学习 【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门 聪明的你,一定会想知道更多。哈哈哈哈哈,下面主播就...
241 6
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门