Java线程池ThreadPoolExcutor源码解读详解02-阻塞队列之ArrayBlockingQueue

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,182元/月
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
简介: `ArrayBlockingQueue` 是Java中一个基于数组的并发队列,具有线程安全的性质。以下是其关键信息的摘要:- **继承实现关系**:它扩展了`AbstractQueue`并实现了`BlockingQueue`接口,确保线程安全的入队和出队操作。- **数据结构**:内部由固定大小的数组支撑,有`takeIndex`和`putIndex`跟踪元素的添加和移除位置,`count`记录队列中的元素数量。- **特点**:队列长度在创建时必须指定且不可变,遵循先进先出(FIFO)原则,当队列满时,添加元素会阻塞,空时,移除元素会阻塞。

 


一、继承实现关系图

image.png

二、低层数据存储结构

public class ArrayBlockingQueue extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    ...
    
    final Object[] items;
    int takeIndex;
    int putIndex;
    int count;
    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;
    
    ...
}

image.gif

说明

  • items: 排队
  • takeIndex: 指向队列下一条数据
  • putIndex: 指向队列下一个put的位置
  • count: 队列的数据的数量
  • lock: 添加删除操作对象锁
  • notEmpty: 队列非空阻塞和唤醒条件
  • notFull: 队列是否已满阻塞和唤醒条件

三、特点及优缺点

2.1 特点

  • 是数组实现的线程安全的有界的阻塞队列
  • 线程安全:公用ReentrantLock锁对象来保证多线程间对资源竞争是互斥的
  • 有界:数组是有界的
  • 阻塞:队列空时移除阻塞,队列满时添加会阻塞
  • 先进先出原则
  • 从尾部插入,从头部取出

2.2 优缺点

  • 初始时指定数组大小
  • 存储空间是预先分配
  • 过程中内存开销较小
  • 公用锁保证线程安全,出列入队不能同时进行
  • 效率低

四、源码详解

读取部分源码:

  • 添加任务方法
  • 获取和删除任务方法

4.1 添加任务

/**
 * 如果有足够的空间,则直接把任务插入到队列尾声部 并 返回true <br/>
 * 如果空间不足,则抛IllegalStateException异常 <br/>
 */ 
public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}
/**
 * 添加任务 <br/>
 * 添加任务过程中,尝试获取锁时,允许其它线程中断并抛出InterruptedException异常 <br/>
 */
public void put(E e) throws InterruptedException {
    // 非空判断
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    // 尝试获取锁,允许在尝试获取锁时其它线程调用尝试获取锁的线程的Thread.interrupt方法来中断线程,这时不用获取到锁,直接抛出InterruptedException
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            // 若队列已满,则等待
            notFull.await();
        // 队列有空间 且被唤醒,则添加到队列尾部
        enqueue(e);
    } finally {
        // 释放锁
        lock.unlock();
    }
}
/**
 * 如果有足够的空间,则直接把任务插入到队列尾声部 并 返回true
 * 如果空间不足,则返回false
 */ 
public boolean offer(E e) {
    // 非空校验
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    // 获取对象锁
    lock.lock();
    try {
        // 判断队列是否已满
        if (count == items.length)
            return false;
        else {
            // 将任务插入到队列尾部
            enqueue(e);
            // 返回true 表示插入成功
            return true;
        }
    } finally {
        // 释放锁
        lock.unlock();
    }
}
/**
 * 将元件插入到当前放放位置
 */
private void enqueue(E e) {
    final Object[] items = this.items;
    items[putIndex] = e;
    if (++putIndex == items.length) putIndex = 0;
    count++;
    // 唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回
    notEmpty.signal();
}

image.gif

4.2 获取并删除任务

/**
 * 从队列中取数据 <br/>
 * 如果队列为空,则返回null <br/>
 */
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return (count == 0) ? null : dequeue();
    } finally {
        // 释放锁
        lock.unlock();
    }
}
/**
 * 从队列中取数据 <br/>
 * 取任务过程中,尝试获取锁时,允许其它线程中断并抛出InterruptedException异常 <br/>
 */
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    // 尝试获取锁,允许在尝试获取锁时其它线程调用尝试获取锁的线程的Thread.interrupt方法来中断线程,这时不用获取到锁,直接抛出InterruptedException
    lock.lockInterruptibly();
    try {
        while (count == 0)
            // 若队列为空,则等待
            notEmpty.await();
            // 队列有数据 且被唤醒,则从队列头取数据
        return dequeue();
    } finally {
        // 释放锁
        lock.unlock();
    }
}
/**
 * 从队列头取一个数据 <br/>
 */
private E dequeue() {
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E e = (E) items[takeIndex];
    items[takeIndex] = null;
    if (++takeIndex == items.length) takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();
    return e;
}

image.gif

五、作用

1. 线程池的线程是有限的,新的任务缓存到队列中
2. 无空闲核心线程情况下,新任务缓存到队列中,可起到控制并发量的作用
3. 在高并发情况下,保证线程数控制有一定范围内,从而提高系统的性能和稳定性

image.gif

六、示例

// 核心线程数
int corePoolSize = 10;
// 最大线程数
int maximumPoolSize = 20;
// 空闲线程等待任务存活时间
long keepAliveTime = 10L;
// keepAliveTime的时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 阻塞队列
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(100);
// 创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, blockingQueue);

image.gif

详细的参数说明上一篇文章


相关文章
|
6月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
461 7
|
6月前
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
1月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
5月前
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
178 6
家政系统源码,java版本
|
5月前
|
供应链 JavaScript 前端开发
Java基于SaaS模式多租户ERP系统源码
ERP,全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统,它通过信息技术手段,将企业的各个业务流程和资源管理进行整合,以提高企业的运营效率和管理水平,它是一种先进的企业管理理念和信息化管理系统。 适用于小微企业的 SaaS模式多租户ERP管理系统, 采用最新的技术栈开发, 让企业简单上云。专注于小微企业的应用需求,如企业基本的进销存、询价,报价, 采购、销售、MRP生产制造、品质管理、仓库库存管理、财务应收付款, OA办公单据、CRM等。
335 23
|
4月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
219 3
|
5月前
|
安全 Java 调度
Netty源码—3.Reactor线程模型二
本文主要介绍了NioEventLoop的执行总体框架、Reactor线程执行一次事件轮询、Reactor线程处理产生IO事件的Channel、Reactor线程处理任务队列之添加任务、Reactor线程处理任务队列之执行任务、NioEventLoop总结。
|
5月前
|
安全 Java
Netty源码—2.Reactor线程模型一
本文主要介绍了关于NioEventLoop的问题整理、理解Reactor线程模型主要分三部分、NioEventLoop的创建和NioEventLoop的启动。
|
6月前
|
Java
【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
本文深入解析了ConcurrentHashMap的实现原理,涵盖JDK 7与JDK 8的区别、静态代码块、构造方法、put/get/remove核心方法等。JDK 8通过Node数组+链表/红黑树结构优化并发性能,采用CAS和synchronized实现高效锁机制。文章还详细讲解了hash计算、表初始化、扩容协助及计数更新等关键环节,帮助读者全面掌握ConcurrentHashMap的工作机制。
143 6
【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
|
6月前
|
Java 关系型数据库 MySQL
Java汽车租赁系统源码(含数据库脚本)
Java汽车租赁系统源码(含数据库脚本)
131 4