首页 > 其他分享 >数组类型的有界阻塞队列-ArrayBlockingQueue

数组类型的有界阻塞队列-ArrayBlockingQueue

时间:2024-05-25 10:29:46浏览次数:23  
标签:队列 lock 元素 阻塞 线程 有界 ArrayBlockingQueue

一:ArrayBlockingQueue简介

  一个由循环数组支持的有界阻塞队列。它的本质是一个基于数组的BlockingQueue的实现。 它的容纳大小是固定的。此队列按 FIFO(先进先出)原则对元素进行排序。 队列的头部是在队列中存在时间最长的元素。队列的尾部是在队列中存在时间最短的元素。

  新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。 这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。 一旦创建了这样的缓存区,就不能再增加其容量。

  此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。 默认情况下,不保证是这种排序。然而通过在构造函数将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程(其公平性是通过ReentrantLock来实现公平锁的)。 公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。

特点:

  • 它是有界阻塞队列。它是数组实现的,是一个典型的“有界缓存区”。数组大小在构造函数指定,而且从此以后不可改变。
  • 它是线程安全的,是阻塞的。
  • 不接受 null 元素
  • 公平性 (fairness)可以在构造函数中指定。
  • 试图向已满队列中放入元素会导致放入操作受阻塞,直到BlockingQueue里有新的唤空间才会被醒继续操作。
  • 试图从空队列中检索元素将导致类似阻塞,直到BlocingkQueue进了新货才会被唤醒。
  • 其容量在构造函数中指定。容量不可以自动扩展,也没提供手动扩展的接口。
  • 它实现了BlockingQueue接口。
  • 此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选方法。

ArrayBlockingQueue的几种构造函数:

 public ArrayBlockingQueue(int capacity)
 public ArrayBlockingQueue(int capacity, boolean fair) 
 public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) 

fair如果为true,则按照 FIFO 顺序访问插入或移除时受阻塞线程的队列;如果为 false,则访问顺序是不确定的。

二:ArrayBlockingQueue源码分析

一个基本数组的阻塞队列。可以设置列队的大小。

它的基本原理实际还是数组,只不过存、取、删时都要做队列是否满或空的判断。然后加锁访问。

1:ArrayBlockingQueue的lock

  首先成员变量有一个Lock和两个Condition的定义及初始化过程如下:
ArrayBlockingQueue的原理就是使用一个可重入锁和这个锁生成的两个条件对象进行并发控制。ArrayBlockingQueue是一个带有长度的阻塞队列,初始化的时候必须要指定队列长度,且指定长度之后不允许进行修改。

/** Main lock guarding all access */
    final ReentrantLock lock;
    /** Condition for waiting takes */
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;
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();
    }

说明fair是“可重入的独占锁(ReentrantLock)”的类型。fair为true,表示是公平锁;fair为false,表示是非公平锁。notEmptynotFull是锁的两个Condition条件。

Lock的作用是提供独占锁机制,来保护竞争的资源;而Condition是为了更精细的对锁进行控制,但是依赖于lock,通过某个条件对多线程进行控制。

ArrayBlockingQueue只有1个锁,添加数据和删除数据的时候只能有1个被执行,不允许并行执行。

notEmpty表示"锁的非空条件"。当某线程想从队列中获取数据的时候,而此时队列中的数据为空,则该线程通过notEmpty.await()方法进行等待;当其他线程向队列中插入元素之后,就调用notEmpty.signal()方法进行唤醒之前等待的线程。

同理notFull表示“锁满的条件“。当某个线程向队列中插入元素,而此时队列已满时,该线程等待,即阻塞通过notFull.wait()方法;其他线程从队列中取出元素之后,就唤醒该等待的线程,这个线程调用notFull.signal()方法。

ArrayBlockingQueue使用ReentrantLock来实现的添加元素原子操作,具体可以看poll和offer中的阻塞awaitNanos(nanos)是使用了Condition中的AQS中的一个方法。

2:入队

  增加元素有多种方法add(),offer(),put(),其中add()方法调用offer()方法。所以只用关注offer()put()方法即可,offer()put()不同点是,offer()在队列满时,直接返回false,不会阻塞写入线程。而put()在队列满时,会一直阻塞写入线程,直到有存储空间可以存放元素。

/** 
     * 添加一个元素,其实super.add里面调用了offer方法 
     */  
    public boolean add(E e) {  
        return super.add(e);  
    }  
  
    /** 
     *加入成功返回true,否则返回false 
     *  
     */  
    public boolean offer(E e) {  
        checkNotNull(e);  
        final ReentrantLock lock = this.lock;  
        lock.lock();//上锁  
        try {  
            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();//和lock()方法的区别是让它在阻塞时也可抛出异常跳出  
        try {  
            while (count == items.length)  
                notFull.await(); //这里就是阻塞了,要注意。如果运行到这里

标签:队列,lock,元素,阻塞,线程,有界,ArrayBlockingQueue
From: https://blog.csdn.net/weixin_41987908/article/details/139183476

相关文章

  • 单调队列&&滑动窗口
    单调队列(MonotonicQueue)是一种特殊的数据结构,可以在常数时间内进行一系列操作,如插入元素、删除元素和获取最大值或最小值。单调队列通常用于解决滑动窗口类问题,其中需要在窗口中维护一些特定性质,例如最大值、最小值或其他聚合函数的值。它具有以下特性:单调性质:单调队列中......
  • 第三讲 栈、队列和数组 (1)
    文章目录第三讲栈、队列和数组3.1栈3.1.1出栈元素的不同排列与卡特兰数3.1.2栈的顺序表实现3.1.3共享栈3.1.4栈的链表实现3.1.5栈的两种实现的优缺点3.1.6c++中的栈(s......
  • 代码随想录算法训练营第三十六天|860.柠檬水找零、406.根据身高重建队列、452. 用最少
    860.柠檬水找零文档讲解:代码随想录题目链接:.-力扣(LeetCode)注意看提示:bills[i] 不是 5 就是 10 或是 20 场景较为固定遇到了20,优先消耗10classSolution:deflemonadeChange(self,bills:List[int])->bool:total={5:0,10:0,20:0}......
  • Volcano社区新版本发布!7大功能全面增强队列能力与调度稳定性
    本文分享自华为云社区《Volcano社区v1.9.0版本正式发布!全面增强队列能力与调度稳定性》,作者:云容器大未来。北京时间2024年5月21日,Volcano社区v1.9.0版本正式发布,此次版本增加了以下新特性:支持弹性队列容量capacity调度支持队列与节点间的亲和调度Volcano支持Kubernet......
  • 栈和队列1 顺序栈及基本操作实例(进制转换)
    #include<stdio.h>#include<stdlib.h>#defineINITSIZE100#defineINCREAMENT10 typedefstructSqStack{   int*data;   int*top;   intstacksize;}SqStack;voidInitStack(SqStack*L){   L->data=(int*)malloc(INITSIZE*siz......
  • 【Test 08】优先队列、滑动窗口、DFS
    文章目录1.单词搜索2.除2操作3.dd爱框框1.单词搜索题目链接解题思路:DFS(深度优先遍历),用一个pos记录要匹配单词word的位置,每次与pos进行匹配判断(这样做的好处是不用把答案存下来)注意细节❗:①没有用flag来记录的话,所有在DFS暴搜的时候需要......
  • 27.并发编制【四】互斥锁与队列
    【一】互斥锁(进程间同步)1)概念一种用于多线程编程中控制对方共享资源访问机制为当前进程或线程添加额外的限制,限制当前时间段只能由当前进程使用,当前进程使用完成后才能其他进程继续使用其可保证同一时间只有一个进程在执行关键代码段,从而保证了数据的安全性2)多个进程......
  • 互斥锁,IPC机制,队列,生产者消费者模型
    Ⅰ互斥锁【一】什么是互斥锁互斥锁其实就是一种锁。为当前进程或线程添加额外的限制限制当前时间段只能由当前进程使用,当前进程使用完成后才能其他进程继续使用其作用是保证在同一时刻只有一个线程在访问共享资源,从而避免多个线程同时读写数据造成的问题。互斥锁的基本原......
  • 风险控制1、如果你的项目发布后失败,主要的原因会是什么?2、每个团队列出自己项目中目前
    项目发布失败的主要原因项目发布后失败可能由多种原因导致,这里列出几个主要的:需求不符合市场实际:产品没有满足目标市场的真实需求或者未能准确捕捉到用户的痛点。用户体验不佳:产品界面复杂难用,用户操作困难,导致用户流失。技术问题:产品存在缺陷或技术故障,影响功能实现或性能稳......
  • 【HZERO】事件以及消息队列
    事件以及消息队列服务事件:https://open.hand-china.com/document-center/doc/component/157/18147?doc_id=408820&_back=%2Fdocument-center%2Fsearch%3Fs%3D%25E4%25BA%258B%25E4%25BB%25B6%25E6%259C%258D%25E5%258A%25A1&doc_code=197230事件服务:https://open.hand-china.c......