Java阻塞队列的简单实现

Java基础学习之并发篇:手写阻塞队列ArrayBlockingQueue 学习目标 我们都知道在并发编程中,阻塞队列在多线程中的场景特别有用,比如在生产和消费者模型中,生产者生产数据到队列,队列满时需要阻塞线程,停止生产。消费者消费队列,对队列为空时阻塞线程停止消费,在Java中有提供不同场景的阻塞队列,那么接下来我们将学习 ReentrantLock的Condition原理 BlockingQueue的定义 了解ArrayBlockingQueue的实现 如何手写一个阻塞队列 Condition原理 在学习阻塞队列之前,我们先需要弄清楚ReentrantLock的Cond 阅读详情

来源:

https://blog.biezhi.me/2019/01/simple-blocking-queue.html

Java 并发常用的组件中有一种队列叫阻塞队列(BlockingQueue),当队列为空时,获取元素的线程会阻塞等待直到队列有数据;当队列满时,想要存储元素的线程会阻塞等待直到队列有空间。我们经常会用这种数据结构可以实现生产者、消费者模型。

本文会通过两种方式来实现简单的有界阻塞队列,在最后分别测试不同实现的性能差异。

Monitor 和 Condition

看过 Java 并发相关书籍的同学应该都见过 Monitor 这个词,有人称为监视器也有人叫它管程,不过都是一个意思:一个同步工具,相当于操作系统中的互斥量(mutex),即值为 1 的信号量。

synchronized 关键词的背后就是靠 monitor 实现的,monitor 的重要特点是,同一个时刻,只有一个线程能进入 monitor 中定义的临界区,这使得 monitor 能够达到互斥的效果。但仅仅有互斥的作用是不够的,无法进入 monitor 临界区的线程应该被阻塞,在必要的时候可以被唤醒,所以 Java 提供了 wait 和 notify、notifyAll 的 API 给我们使用。

  • wait(): 让当前线程进入等待队列,同时会释放锁,直到被唤醒。
  • notify(): 从条件队列中随机唤醒一个线程,让它去参与锁竞争
  • notifyAll(): 唤醒条件队列中的所有线程,让它们去参与锁竞争

------

实现同步的一种方式是使用 synchronized 关键字,还可以使用 Lock 接口下的实现来完成,比如 ReentrantLock,它是一把重入锁(synchronized 也是),基于 AQS 并发框架实现。我们可以使用它来进行加锁和释放锁,如果遇到有条件需要阻塞可以使用 Condition API。

  • Lock#newCondition(): 创建一个新的条件
  • Condition#await(): 让当前线程等待
  • Condition#signal(): 唤醒一个等待线程

条件和锁总是息息相关,在没有 Lock 接口的时候你会发现 monitor 机制有一个严重的问题:一把锁只能对应一个条件(也就是只可以做一次 wait),那么在唤醒的时候就可能出现唤醒丢失。举个例子,在两个方法上有不同的条件会导致阻塞,它们持有一把锁,唤醒时候如果用 notify 只会从条件队列选择一个,使用 notifyAll 会带来大量的 CPU 上下文切换和锁竞争,伪代码如下:

 1synchronized void foo() {
 2    while(CONDITION1){
 3        wait();
 4    }
 5    notifyAll();
 6}
 7synchronized void bar() {
 8    while(CONDITION2){
 9        wait();
10    }
11    notifyAll();
12}

具体实现

实现思路

我们通过定义一个 Queue 接口来实现两种队列,该队列是有界队列,使用数组的方式实现,如果你有兴趣也可以使用链表或栈来实现这个队列。提供 put 方法添加元素(满了则阻塞),take 方法弹出元素(没有元素则阻塞)。

定义接口

 1public interface Queue<E> {
 2
 3    // 添加新元素,当队列满则阻塞
 4    void put(E e) throws InterruptedException;
 5
 6    // 弹出队头元素,当队列空则阻塞
 7    E take() throws InterruptedException;
 8
 9    // 队列元素个数
10    int size();
11
12    // 队列是否为空
13    boolean isEmpty();
14
15}

基于 synchronized 的实现

核心思路:

  • 添加元素时队列满则阻塞
  • 弹出元素时队列空则阻塞
  • 添加元素后唤醒消费者
  • 弹出元素后唤醒生产者
 1public class BlockingQueueWithSync<E> implements Queue<E> {
 2
 3    private E[] array;
 4    private int head;  // 队头指针
 5    private int tail;  // 队尾指针
 6
 7    private volatile int size; // 队列元素个数
 8
 9    public BlockingQueueWithSync(int capacity) {
10        array = (E[]) new Object[capacity];
11    }
12
13    @Override
14    public synchronized void put(E e) throws InterruptedException {
15        // 当队列满的时候阻塞
16        while (size == array.length) {
17            this.wait();
18        }
19
20        array[tail] = e;
21        // 队列装满后索引归零
22        if (  tail == array.length) {
23            tail = 0;
24        }
25          size;
26        // 通知其他消费端有数据了
27        this.notifyAll();
28    }
29
30    @Override
31    public synchronized E take() throws InterruptedException {
32        // 当队列空的时候阻塞
33        while (isEmpty()) {
34            this.wait();
35        }
36
37        E element = array[head];
38        // 消费完后从0开始
39        if (  head == array.length) {
40            head = 0;
41        }
42        --size;
43        // 通知其他生产者可以生产了
44        this.notifyAll();
45        return element;
46    }
47
48    @Override
49    public synchronized boolean isEmpty() {
50        return size == 0;
51    }
52
53    @Override
54    public synchronized int size() {
55        return size;
56    }
57
58}

基于 ReentrantLock 的实现

 1public class BlockingQueueWithLock<E> implements Queue<E> {
 2
 3    private E[] array;
 4    private int head;
 5    private int tail;
 6
 7    private volatile int size;
 8
 9    private Lock      lock     = new ReentrantLock();
10    private Condition notFull  = lock.newCondition();
11    private Condition notEmpty = lock.newCondition();
12
13    public BlockingQueueWithLock(int capacity) {
14        array = (E[]) new Object[capacity];
15    }
16
17    @Override
18    public void put(E e) throws InterruptedException {
19        lock.lockInterruptibly();
20        try {
21            // 队列满,阻塞
22            while (size == array.length) {
23                notFull.await();
24            }
25            array[tail] = e;
26            if (  tail == array.length) {
27                tail = 0;
28            }
29              size;
30            notEmpty.signal();
31        } finally {
32            lock.unlock();
33        }
34    }
35
36    @Override
37    public E take() throws InterruptedException {
38        lock.lockInterruptibly();
39        try {
40            // 队列空,阻塞
41            while (isEmpty()) {
42                notEmpty.await();
43            }
44            E element = array[head];
45            if (  head == array.length) {
46                head = 0;
47            }
48            --size;
49            // 通知isFull条件队列有元素出去
50            notFull.signal();
51            return element;
52        } finally {
53            lock.unlock();
54        }
55    }
56
57    @Override
58    public boolean isEmpty() {
59        lock.lock();
60        try {
61            return size == 0;
62        } finally {
63            lock.unlock();
64        }
65    }
66
67    @Override
68    public int size() {
69        lock.lock();
70        try {
71            return size;
72        } finally {
73            lock.unlock();
74        }
75    }
76
77}

对比性能

 1public class Benchmark {
 2
 3    @Test
 4    public void testWithMonitor() {
 5        Queue<Integer> queue = new BlockingQueueWithSync<>(5);
 6        execute(queue);
 7    }
 8
 9    @Test
10    public void testWithCondition() {
11        Queue<Integer> queue = new BlockingQueueWithLock<>(5);
12        execute(queue);
13    }
14
15    private void execute(Queue<Integer> queue) {
16        ExecutorService executorService = Executors.newCachedThreadPool();
17        for (int i = 1; i <= 1000; i  ) {
18            final int finalNum = i;
19            executorService.execute(() -> {
20                try {
21                    queue.put(finalNum);
22                    Integer take = queue.take();
23                    System.out.println("item: "   take);
24                } catch (InterruptedException e) {
25                    e.printStackTrace();
26                }
27            });
28        }
29        executorService.shutdown();
30    }
31
32}

这个测试程序让 2 个队列的可存储的元素数都为 5,开启 1000 个线程进行 put 和 take 操作,运行后查看总耗时。

可以看出,使用 synchronized 的方式性能较差。

img

● java匠人手法-优雅的处理空值

● REST API 的安全基础

● 深入浅出 CAS

● Spring Boot Devtools热部署

● Spring Boot AOP记录用户操作日志

● Spring Boot整合Mongo DB

● 【图文讲解】你一定能看懂的HTTPS原理剖析!

● 基础面试,为什么面试官总喜欢问String?

● Spring Boot Admin 2.2.0发布,支持最新Spring Boot/Cloud之外,新增中文展示!

● 你应该知道的 @ConfigurationProperties 注解的使用姿势,这一篇就够了

img

如有收获,请帮忙转发,您的鼓励是作者最大的动力,谢谢!

本文由博客一文多发平台 OpenWrite 发布!

AutoSAR系列讲解(入门篇)4.1-BSW概述 BSW概述,主要讲解BSW的结构组成 阅读详情

相关推荐

使用 DeepSeek 配合 即梦AI 生成视频的详细教程

访问即梦AI官网,选择“AI作图”功能,将DeepSeek生成的提示词逐条粘贴至输入框,选择适配的模型(如“通用XL Pro”)和参数(如画幅比例、风格强度)1113。“我要制作一个关于普洱茶文化的宣传视频,请设计一个30秒的脚本,包含时间轴、画面描述、文案和分镜提示词,以表格形式输出。:云南古茶树群,清晨薄雾,金色晨光,露珠特写,国风山水画质感,16:9画幅,使用Nikon D850参数模拟513。:结合DeepSeek生成的知识点脚本与即梦AI的插画风格,制作科普动画211。

weixin_48995695的博客 3万+

用Java实现一个简单的阻塞队列LinkedBlockingQueue

package mytest.others.other; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concur...

java开发学习积累 532

二阶系统的带宽(频域分析)

下面以典型I型系统为例,计算系统的开环截止频率和闭环截止频率。设I型系统为: 1:系统的开环截止频率为 2:系统的闭环截止频率 其实我们在工程整定时,把ζ = 0.707,也把系统的带宽近视看成自然震荡角频率即 (wb≈wc≈wnw_{b}\approx w_{c}\approx w_{n}wb​≈wc​≈wn​)对于二阶系统。 ...

wanrenqi的博客 2万+

Java阻塞队列的实现

数据,不仅在消费数据的时候进行唤醒插入阻塞的线程,同时在插入的时候如果容量还没满,也会唤醒。是一个带有长度的阻塞队列,初始化的时候必须要指定队列长度,且指定长度之后。如果要删除数据的索引不是取索引位置,移动元素元素,更新取索引和放索引的值。方法对于队列为空的情况,会阻塞并挂起当前线程,直到有数据加入到队列中。个锁,放锁和拿锁,添加数据和删除数据是可以并行进行的,当然添加数。如果要删除数据的索引是取索引位置,直接删除取索引位置上的数据,:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除。

2301_77783312的博客 1005

使用Power Apps实现SharePoint Online列表的下拉菜单的级联选择

在SharePoint列表中添加数据的时候,下拉菜单的级联选择一直是一个非常实用的功能,尤其是有很多数据的情况,级联选择可以根据上一层的选择,自动过滤下一层的选择,方便用户查找和选择准确的信息。一个最常用的情景是选择供应商的地区和国家,例如有三个列表,一个是供应商所在的大洲,一个是国家,一个是供应商。当用户选择州的时候,会自动列出属于这个州的国家。避免用户迷失在大量的数据中。 这里展示一下如何在...

shrenk的专栏 7812

PSpice 导入 LTspice 仿真实验 --(如何解决PSpice模型无法使用的错误)

参考了网上第三方器件导入LTspice的博客和资料,不论是哪个公司下载的SPICE模型导入LTspice的方法都类似。如果遇到导入报错的情况可以先看一下报错提示,如果通过语法修改实现功能的则说明可以进行仿真,当然性能可能与官方给的模型存在一点出入,但做一个基本的功能验证本人认为还是可以的,总比做出板子用不了或者完全不匹配的要好一些。

伊丽莎白的学习之路 9007

Java 实现简单的阻塞队列

package com.souche.car_dealer; import java.lang.invoke.LambdaConversionException; /** * @author: shichao * @date: 2018/7/18 * @description: 自实现阻塞队列 */ public class MyBlockQueue { private Ob...

Bazainga的博客 587

脑电分析:为什么大脑信号会“变同步”?——事件相关同步化与去同步化 (ERS/ERD) 全景解析

脑电分析中的事件相关同步化(ERS)和去同步化(ERD)是研究大脑动态节律的重要方法,反映特定任务下脑电频段能量的变化。ERD表示神经元活动分散(功率下降),提示脑区活跃处理信息;ERS则反映神经元同步(功率增强),常与静息状态相关。本文详解了ERS/ERD的生理机制、计算流程(包括基线校正和功率谱分析),并提供了Python实现示例,展示如何通过Welch法和STFT进行时频分析,生成ERD/ERS热力图。该技术广泛应用于运动想象脑机接口、癫痫检测等领域,为理解大脑功能状态提供了量化工具。

qq_32516809的博客 1398

Java数据结构——阻塞队列

阻塞队列BlockingQueue是支持两个附加操作的Queue,这两个操作是:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用BlockingQueue是一个接口,该接口一共有以下8个实现类其中我们最常用的就是以下两种①ArrayBlockingQueue 基于数组的有界阻塞队列②LinkedBlockingQueue 基于链表的阻塞阻塞队列下面是BlockingQueue接口中的抽象方法这些方法基本与常规的队列方法一致,这里就不一一做代码演示了。

m0_74808313的博客 2654

【Android调试】高通adb识别不到设备的完整排查指南:从驱动到端口一步到位

本文系统梳理高通(Qualcomm)平台Android设备adb无法识别的完整排查流程。从最基础的USB连接检查开始,到Windows设备管理器中驱动状态的判断、高通USB驱动的正确安装与更新方法,再到adb端口被占用、USB调试未开启、驱动签名校验失败等高频场景的解决方案。每一步均配截图和操作命令,文末附常见报错速查表,适合做系统开发、刷机调试或者Android Framework bringup时遇到adb不通的工程师快速定位。

weixin_44081096的博客 285

Orin 调试GMSL camera 96712手册重点

96712基础理解

乐于分享 9203

Java实现简单的阻塞队列_java condition 实现简单的阻塞队列

上一篇文章介绍了condition的使用方法这一篇文章介绍如何用condition来实现一个简单的阻塞队列 消费者 生产者模式。消费者 生产者模式就是 生产者生产某些对象,消费者来消费这些对象。其中用对象数组来保存这些对象,既然是数组,在初始化的时候需要指定数组的大小。在生产者生产的时候需要检查数组是否已经满了,如果满了,那么生产者会被挂起,等到有消费者消费对象时,再进行生产。当消费者消费的时候,...

weixin_33763709的博客 157

用 Claude Code 快速摸清一个陌生代码库

摘要:Claude Code作为AI编码助手,能帮助开发者快速理解陌生代码库。与传统代码搜索不同,它从项目根目录出发,通过分析目录结构、配置文件等技术栈信息,提供系统级的概览。最佳实践是先通过"give me an overview"获取整体架构,再深入数据模型、认证流程等核心模块。它能生成术语表、编码规范和推荐的代码阅读路径,极大降低新成员的学习成本。相比传统方式,Claude Code能主动探索项目上下文,无需人工整理相关文件,使代码理解过程更接近有经验的工程师带教。(149字)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 96

java 手写阻塞队列_Java阻塞队列的简单实现

来源:Java 并发常用的组件中有一种队列叫阻塞队列(BlockingQueue),当队列为空时,获取元素的线程会阻塞等待直到队列有数据;当队列满时,想要存储元素的线程会阻塞等待直到队列有空间。我们经常会用这种数据结构可以实现生产者、消费者模型。本文会通过两种方式来实现简单的有界阻塞队列,在最后分别测试不同实现的性能差异。Monitor 和 Condition看过 Java 并发相关书籍的同学应该...

weixin_39627144的博客 282

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

java 简单阻塞队列,java condition 实现简单的阻塞队列

上一篇文章介绍了condition的使用方法这一篇文章介绍如何用condition来实现一个简单的阻塞队列 消费者 生产者模式。消费者 生产者模式就是 生产者生产某些对象,消费者来消费这些对象。其中用对象数组来保存这些对象,既然是数组,在初始化的时候需要指定数组的大小。在生产者生产的时候需要检查数组是否已经满了,如果满了,那么生产者会被挂起,等到有消费者消费对象时,再进行生产。当消费者消费的时候,...

weixin_39971132的博客 153

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Java实现简单的阻塞队列

class MyBlockingQueue { //这里队列底层用数组实现 private int[] queue = new int[1000]; //创建size变量记录队列现有元素的数量 private int size = 0; //创建队头指针 private int head = 0; //创建队尾指针 private int tail = 0; //创建锁对象 private Object locker = new

begefefsef的博客 953

Java实现简单的阻塞队列,在Java中实现自己的阻塞队列

I know this question has been asked and answered many times before, but I just couldn't figure out a trick on the examples found around internet, like this or that one.Both of these solutions check fo...

weixin_39641450的博客 95

阻塞队列 - java基于链表的简单实现

1、阻塞队列的原理 阻塞队列与普通队列的区别在于:阻塞队列为空时,从队列中获取元素的操作将会被阻塞,当队列为满时,往队列里添加元素的操作会被阻塞。 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来 2、阻塞队列的简单实现 /** * 基于链表实现的一...

weixin_30364147的博客 297

用 Java 实现一个简单的阻塞队列

这篇文章分析阻塞队列的实现原理,并给出基于单锁 & 单条件变量的实现方案

lybugproducer的博客 428

使用Java的等待/通知机制实现一个简单的阻塞队列

本文是对Java的等待/通知机制的简单说明,以及使用等待/通知机制完成的一个简单的阻塞队列,并且是借助生产者消费者来测试。

m0_62740234的博客 790

linux 任务队列 链表,阻塞队列 - Java基于链表的简单实现

1、阻塞队列的原理阻塞队列与普通队列的区别在于:阻塞队列为空时,从队列中获取元素的操作将会被阻塞,当队列为满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来2、阻塞队列的简单实现/*** 基于链表实现的一个阻塞队列**@author...

weixin_36176386的博客 186

JAVA实现简单的阻塞队列

public class MyBlockQueue&lt;T&gt; { //push锁 private final static Object pushLock = new Object(); //pop锁 private final static Object popLock = new Object(); //数据存储 private St...

sinat_35738705的博客 4361

Java:实现简单的阻塞队列

JDK中自带ArrayBlockingQueue、ListBlockingQueue等阻塞的队列。我们也可以认为自己实现一个。public class BlockQueue<T> { // push的锁 private final static Object pushLock = new Object(); // pop的锁 private final static O

new_Aiden的博客 7789

java 环形阻塞队列简单实现

java 阻塞队列简单实现/** * 简单的阻塞队列 * @author Administrator * */ public class MySelfBlockingQueue { int capacity = 10; int[] container; int headIndex = 0;//队列头部位置 int tailIndex = 0;//队列尾部位置

u010662108的博客 351

Java多线程案例之阻塞队列

本篇文章将介绍Java多线程案例,阻塞队列,阻塞队列在普通队列的基础上多了两种情况,一是阻塞队列为空时,如果进行出队操作,会使当前线程阻塞,直到有新元素插入阻塞队列,该线程才被通知继续执行出队操作;二是阻塞队列为满时,如果进行入队操作,会使当前线程阻塞,直到有元素出队时,该线程才会被通知继续执行入队操作。在实际开发中,常常使用消息队列,而消息队列就是阻塞队列,只是在阻塞队列的基础上增加了很多功能。

未见花闻的博客 4918

java 使用ReentrantLock Condition实现阻塞队列

阻塞队列简单实现。先进先出

空山清泉明月 3033

阻塞队列的相关问题

阻塞队列是一种特殊的队列,支持在队列为空或满时的阻塞操作。相比之下,非阻塞队列则不会阻塞线程,而是通过返回 null 或抛出异常来表示队列为空或满。

qq_41296669的博客 650
上一篇: Spring Boot AOP记录用户操作日志
下一篇: REST API 的安全基础
SpringForAll
博客等级 码龄7年 15粉丝 79原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值