多线程篇(阻塞队列- ArrayBlockingQueue)(持续更新迭代)

news2024/11/5 22:37:09

目录

一、源码分析

1. 先看个关系图

2. 构造方法

3. 核心属性

4. 核心功能

入队(放入数据)

出队(取出数据)

5. 总结


一、源码分析

1. 先看个关系图

PS:先看个关系图

ArrayBlockingQueue是最典型的有界阻塞队列,其内部是用数组存储元素的,

初始化时需要指定容量大小利用 ReentrantLock 实现线程安全。

在生产者-消费者模型中使用时,如果生产速度和消费速度基本匹配的情况下,使用

ArrayBlockingQueue是个不错选择;

当如果生产速度远远大于消费速度,则会导致队列填满,大量生产线程被阻塞。

使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有一个

线程可以进行入队或者出队操作;

这也就意味着生产者和消费者无法并行操作,在高并发场景下会成为性能瓶颈。

PS:从图中可以看有2个指针,插入和获取。

ArrayBlockingQueue使用

BlockingQueue<String> queue = new ArrayBlockingQueue(1024);
        queue.put("1");   //向队列中添加元素
        String take = queue.take(); // 获取元素

ArrayBlockingQueue特性

2. 构造方法

 public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

    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();
    }

参数:

  • capacity:定义数组大小
  • fair:true/false(公平、非公平锁)

默认是非公平锁的实现

3. 核心属性

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /** The queued items */
    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;

    /*
     * Concurrency control uses the classic two-condition algorithm
     * found in any textbook.
     */

    /** Main lock guarding all access */
    final ReentrantLock lock;

    /** Condition for waiting takes */
    private final Condition notEmpty;

    /** Condition for waiting puts */
    private final Condition notFull;
}

属性说明

  • items:存放元素的数组。
  • takeIndex:获取的指针位置。
  • putIndex:插入的指针位置。
  • count:队列中的元素个数。
  • lock:内部锁。
  • notEmpty:消费者
  • notFull:生产者

4. 核心功能

入队(放入数据)

public void put(E e) throws InterruptedException {
    //检查是否为空
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    //加锁,如果线程中断抛出异常 
    lock.lockInterruptibly();
    try {
       //阻塞队列已满,则将生产者挂起,等待消费者唤醒
       //设计注意点: 用while不用if是为了防止虚假唤醒
        while (count == items.length)
            notFull.await(); //队列满了,使用notFull等待(生产者阻塞)
        // 入队
        enqueue(e);
    } finally {
        lock.unlock(); // 唤醒消费者线程
    }
}
    
private void enqueue(E x) {
    final Object[] items = this.items;
    //入队   使用的putIndex
    items[putIndex] = x;
    if (++putIndex == items.length) 
        putIndex = 0;  //设计的精髓: 环形数组,putIndex指针到数组尽头了,返回头部
    count++;
    //notEmpty条件队列转同步队列,准备唤醒消费者线程,因为入队了一个元素,肯定不为空了
    notEmpty.signal();
}

put主要做了几件事情

1、加中断锁lockInterruptibly(和普通lock一样,只不过多了个判断,如果被中断,则抛异常)。

2、判断队列中的数组长度元素已满,满了则让生产者阻塞。

3、没满,则入队,调用enqueue方法

4、解锁

enqueue主要做了几件事情

1、入队时,把元素放在当前putIndex指针的位置上。

2、看下一指针位置是不是到了队尾,如果是,则改为0(设计的精髓: 环形数组,putIndex指针

到数组尽头了,返回头部)。

3、数组元素+1

4、唤醒消费者线程,因为入队了一个元素,肯定不为空了。

出队(取出数据)

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    //加锁,如果线程中断抛出异常 
    lock.lockInterruptibly();
    try {
       //如果队列为空,则消费者挂起
        while (count == 0)
            notEmpty.await();
        //出队
        return dequeue();
    } finally {
        lock.unlock();// 唤醒生产者线程
    }
}
private E dequeue() {
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex]; //取出takeIndex位置的元素
    items[takeIndex] = null;
    if (++takeIndex == items.length)
        takeIndex = 0; //设计的精髓: 环形数组,takeIndex 指针到数组尽头了,返回头部
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    //notFull条件队列转同步队列,准备唤醒生产者线程,此时队列有空位
    notFull.signal();
    return x;
}

take主要做了几件事情

1、添加中断锁。

2、判断队列是否为空,为空,消费者则挂起

3、如果不为空,则出队,调用dequeue方法

4、解锁

dequeue主要做了几件事情

1、取出takeIndex指针位置的元素。

2、看指针位置是不是到了队尾,如果是,则置0,下一次从头在拿。

3、唤醒生产者线程,此时队列有空位

5. 总结

ArrayBlockingQueue 是一个环形数组,通过维护队首、队尾的指针,来优化(避免了数组的插入

和删除,带来的数组位置移动)插入、删除,从而使时间复杂度为O(1).

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

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

相关文章

CSDN文章无水印转成PDF

文章目录 一、打开检查二、点击进入控制台三、在控制台中输入代码 一、打开检查 f11或者右键打开检查 二、点击进入控制台 三、在控制台中输入代码 (function(){ use strict;var articleBox $("div.article_content");articleBox.removeAttr("style&quo…

sping boot 基于 RESTful 风格,模拟增删改查操作

RESTful -> 增&#xff1a;post 删&#xff1a;delete 改: put 查: get RESTful 资源路径&#xff0c;一般以 s 复数结尾 以下是代码示例&#xff1a; package com.example.springboot.controller;import org.springframework.web.bind.annotation.*;RestControll…

EmguCV学习笔记 C# 9.3 移动检测类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

汽车网络安全的未来:将车辆视为端点

汽车行业面临着许多与其他行业的成功企业相同的网络安全风险和威胁&#xff0c;但它也在应对一些独特的风险和威胁。 Nuspire 的首席威胁分析师 Josh Smith&#xff08;一家在汽车领域有着深厚根基并保护通用汽车和斯巴鲁等客户的托管安全服务提供商&#xff09;谈到了当前的风…

【AcWing】852. spfa判断负环

#include<iostream> #include<algorithm> #include<cstring> #include<queue> using namespace std;const int N 1e510;int n,m; int h[N],w[N],e[N],ne[N],idx; int dist[N],cnt[N];//cnt存最短路径的边数 bool st[N];void add(int a,int b,int c){e[…

一文讲懂扩散模型

一文讲懂扩散模型 扩散模型&#xff08;Diffusion Models, DM&#xff09;是近年来在计算机视觉、自然语言处理等领域取得显著进展的一种生成模型。其思想根源可以追溯到非平衡热力学&#xff0c;通过模拟数据的扩散和去噪过程来生成新的样本。以下将详细阐述扩散模型的基本原理…

发动机制造5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

发动机制造作为高端制造业的核心领域之一&#xff0c;正积极探索并引领这一变革。其中&#xff0c;发动机制造5G智能工厂物联数字孪生平台的兴起&#xff0c;不仅为发动机制造业注入了新的活力&#xff0c;也为整个制造业的数字化转型树立了新的标杆。发动机制造5G智能工厂物联…

Linux Centos 7网络配置

本步骤基于Centos 7&#xff0c;使用的虚拟机是VMware Workstation Pro&#xff0c;最终可实现虚拟机与外网互通。如为其他发行版本的linux&#xff0c;可能会有差异。 1、检查外网访问状态 ping www.baidu.com 2、查看网卡配置信息 ip addr 3、配置网卡 cd /etc/sysconfig…

致远个性化之--发起流程页面,去掉【查看流程】按钮

需求 近期在做的项目中&#xff0c;遇到一个需求&#xff0c;想把发起流程页面中的【查看流程】按钮去掉&#xff0c;只让员工预测流程&#xff0c;知道自己的事项流程走向&#xff0c;不让看全局流程图。包含PC端和移动端&#xff0c;以及微协同端。 如下图效果示例&#xff1…

SVN下载安装使用方法

目录 &#x1f315;SVN是什么&#xff1f;&#x1f319;SVN跟Git比的优势&#x1f319;SVN的用处 &#x1f315;下载安装使用方法 &#x1f315;&#x1f319;⭐ &#x1f315;SVN是什么&#xff1f; 代码版本管理工具 它能记住你每次的修改 查看所有的修改记录 恢复到任何历…

如何读.Net Framework 的源码?

.Net Framework的源码可以从这里下载 Download 也可以在线直接浏览 https://referencesource.microsoft.com 这里我们以System.IO.Directory.CreateDirectory函数为例&#xff0c;来说明如何去读.Net Framework的源码。 在ReferenceSource在线界面的搜索框里输入Directory.Cr…

C语言深度剖析--不定期更新的第四弹

哈哈哈哈哈哈&#xff0c;今天一天两更&#xff01; void关键字 void关键字不能用来定义变量&#xff0c;原因是void本身就被编译器解释为空类型&#xff0c;编译器强制地不允许定义变量 定义变量的本质是&#xff1a;开辟空间 而void 作为空类型&#xff0c;理论上不应该开…

NLP自然语言处理学习过程中知识点总结

OOV是什么 OOV 是 “Out Of Vocabulary”的缩写&#xff0c;意思是 “超出词汇表” 或 “未登录词汇”。 在自然语言处理 (NLP) 中&#xff0c;OOV 指的是模型训练时没有见过的词语或词汇。通常&#xff0c;语言模型会为其训练数据中未出现的词汇分配一个特殊的标记。OOV 词汇…

【国赛急救包】数模国赛查重规则及降重技巧

国赛已经快接近尾声了&#xff0c;各位宝宝论文写得怎么样啦~ 今天为大家分享关于国赛查重的一些规则&#xff0c;以及降重技巧&#xff01;快收藏起来吧~ 1. 国赛查重要求及如何查重 • 数学建模国赛的查重除了知网数据库以外&#xff0c;更重要的是自建库的查重比对&#x…

14.1 为什么说k8s中监控更复杂了

本节重点介绍 : k8s中监控变得复杂了&#xff0c;挑战如下 挑战1: 监控的目标种类多挑战2: 监控的目标数量多挑战3: 对象的变更和扩缩特别频繁挑战4: 监控对象访问权限问题 k8s架构图 k8s中监控变得复杂了&#xff0c;挑战如下 挑战1: 监控的目标种类多 对象举例 podnodese…

【kubernetes】配置管理中心Configmap运用

一&#xff0c;介绍 Configmap&#xff08;简写 cm&#xff09;是k8s中的资源对象&#xff0c;用于保存非机密性的配置的&#xff0c;数据可以用key/value键值对的形式保存&#xff0c;也可通过文件的形式保存。 【局限性】&#xff1a;在ConfigMap不是用来保存大量数据的&am…

Windows下Python和PyCharm的应用(二)__快捷键方式的设定

前言 程序写久了&#xff0c;难免会形成自己的编程习惯。比如对某一套快捷键的使用&#xff0c;已经形成了肌肉记忆。 为了方便快捷键的使用&#xff0c;可以在PyCharm中设置自己喜欢的快捷键。 我比较习惯于微软Visual Studio的快捷键设置。&#xff08;因为早些年VC开发用的…

模具要不要建设3D打印中心

随着3D打印技术的日益成熟与广泛应用&#xff0c;模具企业迎来了自建3D打印中心的热潮。这一举措不仅为企业带来了前所未有的发展机遇&#xff0c;同时也伴随着一系列需要克服的挑战&#xff0c;如何看待企业引进增材制造&#xff0c;小编为您全面分析。 机遇篇&#xff1a; 加…

win11如何录屏

在 Win11 中录屏可以使用系统自带的工具和一些第三方应用。以下是几种方法&#xff1a; 方法一&#xff1a;使用 Xbox Game Bar 1. 打开 Xbox Game Bar - 按 Win G 组合键打开 Xbox Game Bar。 2. 开始录制 - 在显示的界面中&#xff0c;点击“录制”按钮&#xff08;…

Linux之nginx部署项目【前后端分离】(外加redis安装)

nginx安装和访问 1.使用apt安装Nginx apt install -y nginx 用whereis nginx找到和nginx相关目录 nginx目录结构 /usr/sbin/nginx 服务文件 /etc/nginx 配置目录 /usr/share/nginx/html 发部项目 服务名: nginx.service ps -ef | grep nginx apt install -y net-tools …