值得用来替代Vector的Java集合:ArrayBlockingQueue详解

2024-05-12 14:12

本文主要是介绍值得用来替代Vector的Java集合:ArrayBlockingQueue详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在并发编程中,线程之间相互合作执行任务,其中数据传输是至关重要的。对于多个线程访问共享数据的情况下,我们需要保证数据的正确性和一致性。Java提供了多种高效的线程安全容器来满足这种需求。其中一种是 ArrayBlockingQueue,它是一个基于数组的有界队列,可以安全地同时被多个线程使用。

摘要

  本文将介绍 ArrayBlockingQueue 的基本概念、源代码解析、应用场景案例以及优缺点分析。

ArrayBlockingQueue

简介

  ArrayBlockingQueue 是一个有界队列,基于数组实现。它按照先进先出的原则对元素进行排序。当队列已满时,生产者线程将被阻塞,直到有空间可用;当队列为空时,消费者线程将被阻塞,直到有元素可用。

ArrayBlockingQueue 的主要方法如下:

  • put(E e):将指定元素添加到此队列的尾部,如果队列已满则阻塞直到队列有空间可用。
  • take():获取并移除此队列的头元素,如果队列为空则阻塞直到队列有元素可用。
  • offer(E e):将指定元素插入此队列的尾部,如果队列已满则返回 false
  • poll():获取并移除此队列的头元素,如果队列为空则返回 null
  • remainingCapacity():返回此队列中剩余的可用空间。
  • size():返回此队列中的元素数量。

源代码解析

  这里简要分析一下 ArrayBlockingQueue 的源代码。

类定义

public class ArrayBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {private static final long serialVersionUID = -817911632652898426L;final Object[] items; // 存储元素的数组int takeIndex; // 队列头部元素的索引int putIndex; // 队列尾部元素的索引int count; // 已经添加到队列中的元素数量final ReentrantLock lock; // 可重入锁private final Condition notEmpty; // 不空条件private final Condition notFull; // 不满条件// 省略构造方法和其他细节
}

  可以看出 ArrayBlockingQueue 实际上是一个 Object 类型的数组,同时维护了队列头部元素的索引、队列尾部元素的索引、已经添加到队列中的元素数量、可重入锁、不空条件和不满条件等属性。
如下是部分源码截图:

在这里插入图片描述

put() 方法

public void put(E e) throws InterruptedException {Objects.requireNonNull(e); // 对象不能为空final ReentrantLock lock = this.lock;lock.lockInterruptibly(); // 获取可中断锁try {while (count == items.length) { // 队列已满notFull.await(); // 队列不满条件等待}enqueue(e); // 入队} finally {lock.unlock(); // 释放锁}
}

  put() 方法将指定元素添加到队列的尾部。如果队列已满,则阻塞等待直到队列有空间可用。在执行该方法时,线程会获取可中断锁并进入临界区。若队列已满,则线程调用 notFull.await() 方法进入条件等待状态。当其他线程调用 take() 方法或 poll() 方法取走了队列中的元素并释放了空间,就会调用 notEmpty.signal() 方法通知 notFull 等待队列,此时线程会继续从 while 循环中进行判断是否需要继续等待。

如下是部分源码截图:

在这里插入图片描述

take() 方法

public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly(); // 获取可中断锁try {while (count == 0) { // 队列为空notEmpty.await(); // 队列不空条件等待}return dequeue(); // 出队} finally {lock.unlock(); // 释放锁}
}

  take() 方法获取并移除队列头部的元素,如果队列为空,则阻塞等待直到队列有元素可用。在执行该方法时,线程会获取可中断锁并进入临界区。若队列为空,则线程调用 notEmpty.await() 方法进入条件等待状态。当其他线程调用 put() 方法或 offer() 方法插入了元素并释放了空间,就会调用 notFull.signal() 方法通知 notEmpty 等待队列,此时线程会继续从 while 循环中进行判断是否需要继续等待。

如下是部分源码截图:

在这里插入图片描述

应用场景案例

  假设一个场景,有一个生产者和多个消费者,生产者不断往队列中添加元素,消费者不断从队列中取出元素进行处理。

import java.util.concurrent.ArrayBlockingQueue;public class ProducerConsumer {public static void main(String[] args) {ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);new Thread(() -> {for (int i = 1; i <= 20; i++) {try {queue.put(i);System.out.println("生产了:" + i);} catch (InterruptedException e) {e.printStackTrace();}}}).start();new Thread(() -> {while (true) {try {Integer num = queue.take();System.out.println("消费了:" + num);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

  在上述程序中,我们创建了一个容量为 10 的 ArrayBlockingQueue 队列。生产者线程不断向队列中添加元素,消费者线程不断从队列中获取元素进行消费。当队列已满时,生产者线程将被阻塞,直到队列中有空间可用;当队列为空时,消费者线程将被阻塞,直到队列中有元素可用。

  根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。

在这里插入图片描述

优缺点分析

优点

  • ArrayBlockingQueue 是线程安全的,可以安全地同时被多个线程使用。
  • 它具有高效的入队和出队操作,可以快速地插入和删除数据。
  • 由于它是一个有界队列,因此它可以帮助我们避免 OutOfMemoryError 的问题,防止无限制地添加数据。

缺点

  • ArrayBlockingQueue 的容量是固定的,因此在某些场景下可能会受到限制。

类代码方法介绍

下面是 ArrayBlockingQueue 类的主要方法介绍:

方法名描述
put(E e)将指定元素添加到此队列的尾部,如果队列已满则阻塞直到队列有空间可用。
take()获取并移除此队列的头元素,如果队列为空则阻塞直到队列有元素可用。
offer(E e)将指定元素插入此队列的尾部,如果队列已满则返回 false
poll()获取并移除此队列的头元素,如果队列为空则返回 null
remainingCapacity()返回此队列中剩余的可用空间。
size()返回此队列中的元素数量。
iterator()返回此队列元素的迭代器。

测试用例

  根据如上理论知识点对ArrayBlockingQueue的盘点,这里我再给大家做个简单的测试用例进行使用演示:

测试代码演示

package com.example.javase.collection;import java.util.concurrent.ArrayBlockingQueue;/*** @Author ms* @Date 2023-10-24 13:31*/
public class ArrayBlockingQueueTest {public static void main(String[] args) throws InterruptedException {ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);// 生产者线程new Thread(() -> {for (int i = 1; i <= 10; i++) {try {queue.put(i);System.out.println("生产了:" + i);} catch (InterruptedException e) {e.printStackTrace();}}}).start();// 消费者线程new Thread(() -> {while (true) {try {Integer num = queue.take();System.out.println("消费了:" + num);} catch (InterruptedException e) {e.printStackTrace();}}}).start();// 等待一段时间后结束程序Thread.sleep(1000);System.exit(0);}
}

测试结果

  根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。

在这里插入图片描述

代码分析

  根据如上测试用例,在此我给大家进行深入详细的解读一下测试代码,以便于更多的同学能够理解并加深印象。
  如上测试用例演示了使用Java中的ArrayBlockingQueue类实现生产者和消费者模型。

ArrayBlockingQueue是Java中的一个阻塞队列,具有以下特点:

  1. 有固定的容量,一旦队列满了,再往里面放元素就会阻塞直到有元素被取出。

  2. 当队列为空时,从队列中取元素会被阻塞,直到有元素被加入。

  在该代码中,定义了一个ArrayBlockingQueue对象queue,并指定了容量为5。

  然后启动了两个线程,一个是生产者线程,一个是消费者线程。

  生产者线程不断向队列中put元素,消费者线程不断从队列中take元素,实现了生产者和消费者的异步操作。

  最后通过让主线程睡眠一段时间,然后结束程序,来结束整个程序。

小结

  本文介绍了 Java 多线程编程中的 ArrayBlockingQueue,包括其基本概念、源代码解析、应用场景案例以及优缺点分析。总体来说,ArrayBlockingQueue 是一种高效的线程安全容器,适用于生产者消费者场景,能够优化多线程之间的数据传输和共享。不过需要注意的是,它是一个固定容量的有界队列,因此在某些场景下可能会受到限制。

总结

  在多线程编程中,线程之间需要相互协作执行任务,数据传输起着重要的作用。为了保证多个线程访问共享数据时的正确性和一致性,Java提供了多种高效的线程安全容器,其中一个就是 ArrayBlockingQueue

  ArrayBlockingQueue 是一个基于数组实现的有界队列,在多个线程访问共享数据时,可以安全地同时被多个线程使用。它按照先进先出的原则对元素进行排序,当队列已满时,生产者线程将被阻塞,直到队列有空间可用;当队列为空时,消费者线程将被阻塞,直到队列中有元素可用。它还具有高效的入队和出队操作,可以快速地插入和删除数据。

  但是,ArrayBlockingQueue 的容量是固定的,因此在某些场景下可能会受到限制。在实际应用中,我们需要根据具体的需求来选择使用合适的线程安全容器。

  总之,了解和掌握 ArrayBlockingQueue 的使用方法可以帮助我们更好地设计和实现多线程应用,提高程序的并发性和安全性。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

这篇关于值得用来替代Vector的Java集合:ArrayBlockingQueue详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/982845

相关文章

MySQL 主从复制部署及验证(示例详解)

《MySQL主从复制部署及验证(示例详解)》本文介绍MySQL主从复制部署步骤及学校管理数据库创建脚本,包含表结构设计、示例数据插入和查询语句,用于验证主从同步功能,感兴趣的朋友一起看看吧... 目录mysql 主从复制部署指南部署步骤1.环境准备2. 主服务器配置3. 创建复制用户4. 获取主服务器状态5

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更

Redis中Stream详解及应用小结

《Redis中Stream详解及应用小结》RedisStreams是Redis5.0引入的新功能,提供了一种类似于传统消息队列的机制,但具有更高的灵活性和可扩展性,本文给大家介绍Redis中Strea... 目录1. Redis Stream 概述2. Redis Stream 的基本操作2.1. XADD

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建