【源码解析】聊聊阻塞队列之BlockingArrayQueue

news2025/2/2 12:34:40

阻塞队列

阻塞队列:顾名思义 首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下入所示。

  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞。
  • 当阻塞队列时满的时,往队列里添加元素的操作将会被阻塞。

试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。

同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程从队列中移除一个元素才可以插入队列中。

为什么使用阻塞队列 好处?

在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒。

为什么需要BlockingQueue

好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BolckingQueue都给你一手包办了,在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

阻塞队列接口和实现类

实现类特点
ArrayBlockingQueue由数组结构组成的有界阻塞队列
LinkedBlockingQueue由链表结构组成的有界(但大小默认值有Integer.MAX_VALUE)阻塞队列
PriorityBlockingQueue支持优先级排序的无界阻塞队列
DelayQueue使用优先级队列实现的延迟无界阻塞队列
SynchronousQueue不存储元素的阻塞队列,也既单个元素的队列
LinkedTransferQueue由链表结构组成的无界阻塞队列
LinkedBlockingDeque由链表结构组成的双向阻塞队列

BlockingQueue

//阻塞队列
public interface BlockingQueue<E> extends Queue<E> {
   
//   将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量
//  在成功时返回 true,如果此队列已满,则抛IllegalStateException。
    boolean add(E e);

    //插入队列的尾部
    boolean offer(E e);

 		void put(E e) throws InterruptedException;

    //插入队列的尾部,可以设置等待时间,不成功抛出异常
    boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

    //移除对头部元素。如果没有元素会阻塞
    E take() throws InterruptedException;

    //移除对头元素
    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;
}

插入方法:

add(E e) : 添加成功返回true,失败抛IllegalStateException异常
  offer(E e) : 成功返回 true,如果此队列已满,则返回 false。
  put(E e) :将元素插入此队列的尾部,如果该队列已满,则一直阻塞 // 推荐使用这个
删除方法:

remove(Object o) :移除指定元素,成功返回true,失败返回false
  poll() : 获取并移除此队列的头元素,若队列为空,则返回 null
  take():获取并移除此队列头元素,若没有元素则一直阻塞。 //推荐使用这个

ArrayBlockingQueue

内部是通过冲入锁reentrantLock和Condition条件队列实现的,即有公平访问和非公平访问两种方式。

一个测试demo

public class BlockingArrayQueue {

    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(1);
        new Thread(new Product(blockingQueue)).start();
        new Thread(new Consumer(blockingQueue)).start();
    }

}

class Product implements Runnable{

    private BlockingQueue<String> blockingQueue;

    public Product(BlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                System.out.println("添加元素了");
                blockingQueue.put(String.valueOf(UUID.randomUUID()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable{

    private BlockingQueue<String> blockingQueue;

    public Consumer(BlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                String take = blockingQueue.take();
                System.out.println("消费元素了  "+take);
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

构造方法

    //默认是非公平锁的方式
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

	  //可以通过参数进行设置是否使用公平锁
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        //使用的是 ReentrantLock 去判断是否使用公平锁
        lock = new ReentrantLock(fair);
        //使用的condition 非空和非满的条件
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

属性

    /** The queued items */
    // 存储数据的数组 使用的是一个object数组
    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;

    /** Main lock guarding all access */
    //控制并发访问的锁ReentrantLock
    final ReentrantLock lock;

    // notEmpty条件对象,用于通知take方法队列已有元素,可执行获取操作
    /** Condition for waiting takes */
    private final Condition notEmpty;

    // notFull条件对象,用于通知put方法队列未满,可执行添加操作
    /** Condition for waiting puts */
    private final Condition notFull;

    //迭代器
    transient Itrs itrs = null;

添加

   //add方法 其实调用的是offer()
    //加入数据成功 返回true
    //加入失败就抛出异常
    public boolean add(E e) {
        return super.add(e);
    }

    public boolean offer(E e) {
        //判断是否为空
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        //加锁操作
        lock.lock();
        try {
            //如果当前队列满 返回false
            if (count == items.length)
                return false;
            else {
                // 添加元素到队列中
                enqueue(e);
                return true;
            }
        } finally {
            //释放锁
            lock.unlock();
        }
    }


    //阻塞方法
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly(); //该方法可被中断
        try {
            //当队列元素和数组长度相等  当前线程挂起,添加到notFull条件队列中等待唤醒
            while (count == items.length)
                notFull.await();
            //不满则,直接添加元素
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

获取

   public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //队列元素为空 返回null
            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();
        }
    }
    
    
    //直接返回队列头的元素 不进行修改
    public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return itemAt(takeIndex); // null when queue is empty
        } finally {
            lock.unlock();
        }
    }

在这里插入图片描述
put和take是阻塞方法,所以在实际的编程中使用阻塞队列的话,其实更多的还是使用者一组。

至于阻塞和唤醒的原因,其实就是当put 队列满了之后,await 阻塞,队列如果取数据的话,就自动唤醒使用notFull.signal。
同理take()也是一样的,如果获取的元素队列是空,那么就会等待。如果有元素入队列,notEmpty.singal()
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1290071.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【沐风老师】3DMAX切片工具插件安装使用方法详解

3DMAX切片工具安装使用方法 3DMAX切片工具&#xff0c;该工具沿世界坐标轴以规则的间隔对对象进行切片处理。例如&#xff0c;对于快速均匀细分复杂网格、顶点绘制或应用“弯曲”修改器非常有用。 【适用版本】 3dMax2016 - 2023&#xff08;不仅限于此范围&#xff09; 【安装…

Spring Cloud Stream 4.0.4 rabbitmq 发送消息多function

使用 idea 创建 Springboot 项目 添加 Spring cloud stream 和 rabbitmq 依赖 pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchem…

多人聊天室

1.创建服务面板 package yiduiy;import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Has…

国内AI大模型已近80个,哪个最有前途?

目前&#xff0c;国内已经推出了近80个人工智能大模型&#xff0c;这些大模型各有优势&#xff0c;难以直接判断哪个最有前途。然而&#xff0c;以下几个大模型值得关注&#xff1a; 1、华为云盘古大模型&#xff1a;盘古大模型聚焦于为行业服务&#xff0c;包括自然语言、视觉…

基于Vue.js的厦门旅游电子商务预订系统的设计和实现

项目编号&#xff1a; S 030 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S030&#xff0c;文末获取源码。} 项目编号&#xff1a;S030&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 景点类型模块2.2 景点档案模块2.3 酒…

吴恩达《机器学习》11-1-11-2:首先要做什么、误差分析

一、首先要做什么 选择特征向量的关键决策 以垃圾邮件分类器算法为例&#xff0c;首先需要决定如何选择和表达特征向量 &#x1d465;。视频提到的一个示例是构建一个由 100 个最常出现在垃圾邮件中的词构成的列表&#xff0c;根据这些词是否在邮件中出现来创建特征向量&…

Windows磁盘管理中硬盘无法初始化怎么办?

硬盘未出现在“此电脑”选项下的情况并不少见&#xff0c;当您打开磁盘管理&#xff0c;它要么显示为磁盘未知&#xff0c;要么显示为未分配的空间&#xff0c;或者只是不显示磁盘容量。为了访问您的硬盘并充分利用它&#xff0c;您需要对其进行初始化。不幸的是&#xff0c;您…

CTF 6

信息收集 话不多说&#xff0c;nmap进行信息收集&#xff01; 存活主机探测 服务版本探测 端口探测 漏洞脚本探测 UDP端口探测 渗透测试 先看看网站的首页&#xff0c;发现了几个用户&#xff1a; 直接先保存下来吧&#xff0c;以防后面会用到。 SQL注入 看到一个read mor…

行业研究:2023年中国游戏陪玩行业市场现状分析

近年来随着我国游戏行业的不断发展&#xff0c;我国游戏用户规模也是随着稳步上升&#xff0c;给游戏陪玩行业带来了稳定的用户基础。在用户规模增长的同时&#xff0c;随着经济、文化的快速发展&#xff0c;我国娱乐技能社交也随之逐渐兴起。而作为我国娱乐技能社交比重较大的…

01-Redis核心数据结构与高性能原理

一、Redis的单线程和高性能 1. Redis是单线程吗&#xff1f; Redis的单线程主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的&#xff08;说白了也就是执行命令的时候是由一个线程来完成的&#xff09;&#xff0c;这也是 Redis 对外提供键值存储服务的主要流程。…

Jave内存模型 与 CPU硬件架构 的交互图

JMM里所讲的主内存、工作内存与Java内存区域中的Java堆、栈、方法区等并不是同一个层次的对内存的划分&#xff0c;这两者基本上是没有任何关系的。 如果两者一定要勉强对应起来&#xff0c;那么从变量、主内存、工作内存的定义来看&#xff0c;主内存主要对应于Java堆中的对象…

JAVAEE初阶相关内容第十九弹--网络原理之TCP_IP【续集2】

写在前 上一篇博客主要介绍的是关于网络层协议-IP协议的重点介绍。需要掌握关于IP协议的协议头格式&#xff0c;关于IPV4分配不够的解决办法。地址管理与路由选择。 点击跳转上一篇博客 本篇博客将继续学习关于计网中协议的内容。 本篇博客主要介绍关于数据链路层的重点协议-以…

深信服行为管理AC设置禁止用户使用向日葵等远程软件

需求&#xff1a;在特定的时间内禁止内外网用户使用向日葵、todesk等远程软件&#xff1b;只禁止使用专业的远程软件&#xff0c;内网的ssh、telnet、RDP需要正常放行 AC版本&#xff1a;AC13.0.62.001 Build20221107 通过访问权限策略来控制 1、行为管理→访问权限策略→新…

封装校验-----Vue3+ts项目

登录校验页面 <script setup lang"ts"> import { ref } from vue import { mobileRules, passwordRules } from /utils/rules const mobile ref() const password ref() </script><!-- 表单 --><van-form autocomplete"off">&l…

实战oj题——设计循环队列

前言&#xff1a;今天我们来实现循环队列。 各个接口的实现 创建队列&#xff1a; typedef struct {int* a;int front;int back;int k;} MyCircularQueue;我们的队列是由数组储存的&#xff0c;所以我们队列中得定义一个数组&#xff0c;front代表我们的首元素&#xff0c;ba…

项目中使用之Maven BOM

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 工具教程 ✨特色专栏&#xff1a; MyS…

Python中的匿名函数是什么

匿名函数 lambda x , y : xy 1.匿名的目的就是要没有名字&#xff0c;给匿名函数赋给一个名字是没有意义的。 2.匿名函数的参数规则、作用域关系与有名函数是一样的。 3.匿名函数的函数体通常应该是 一个表达式,该表达式必须要有一个返回值。 flambda x,n:x ** n print(f…

Linux 进程地址空间

文章目录 进程地址空间进程地址空间结构页表虚拟内存写时拷贝 进程地址空间 进程地址空间难以定义&#xff0c;因为它更像是一个中间件。 程序从磁盘中加载到内存&#xff0c;程序的执行需要硬件资源&#xff0c;所以每个程序启动时会创建至少一条进程&#xff0c;进程作为组…

[NAND Flash 2.3] 闪存芯片国产进程

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 目录 前言1 闪存介质1.1 NOR 闪存国产技术发展1.2 NAND 闪存国产技术 2 闪存国产厂商与产品2.1 NOR FLASH 国产厂商与产品2.2 NAND FA…

http和https的区别有哪些

目录 HTTP&#xff08;HyperText Transfer Protocol&#xff09; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09; 区别与优势 应用场景 未来趋势 当我们浏览互联网时&#xff0c;我们经常听到两个常用的协议&#xff1a;HTTP&#xff08;HyperText Tra…