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;
AI 代码解读

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();
}
AI 代码解读

在构造函数中会指定初始容量以及锁的类型,默认是非公平锁。
数组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();//唤醒阻塞的获取元素的线程
}
AI 代码解读

整个过程还是比较简单的,首先加锁,注意这里的锁允许中断返回。然后,如果队列满了(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;
}
AI 代码解读

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队列中的线程。

目录
打赏
0
0
0
0
656
分享
相关文章
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
90 7
|
4天前
|
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
46 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
智慧产科一体化管理平台源码,基于Java,Vue,ElementUI技术开发,二开快捷
智慧产科一体化管理平台覆盖从备孕到产后42天的全流程管理,构建科室协同、医患沟通及智能设备互联平台。通过移动端扫码建卡、自助报道、智能采集数据等手段优化就诊流程,提升孕妇就诊体验,并实现高危孕产妇五色管理和孕妇学校三位一体化管理,全面提升妇幼健康宣教质量。
45 12
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
36 4
基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
71 6
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
106 7
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
171 13
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
89 12
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
174 1
家政上门系统用户端、阿姨端源码,java家政管理平台源码
家政上门系统基于互联网技术,整合大数据分析、AI算法和现代通信技术,提供便捷高效的家政服务。涵盖保洁、月嫂、烹饪等多元化服务,支持多终端访问,具备智能匹配、在线支付、订单管理等功能,确保服务透明、安全,适用于家庭生活的各种需求场景,推动家政市场规范化发展。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等