Java 中LinkedBlockingQueue和ArrayBlockingQueue

2024-02-21 14:28

本文主要是介绍Java 中LinkedBlockingQueue和ArrayBlockingQueue,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是LinkedBlockingQueue和ArrayBlockingQueue

LinkedBlockingQueue和ArrayBlockingQueue都是Java中常用的阻塞队列(BlockingQueue)实现类。它们的主要区别和特点如下:

  1. 数据结构:LinkedBlockingQueue基于链表实现,而ArrayBlockingQueue基于数组实现。
  2. 容量限制:LinkedBlockingQueue在创建时可以指定一个可选的容量参数,如果不指定容量,则默认容量为Integer.MAX_VALUE,因此它可以持续地添加任务,不会抛出队列满的异常。而ArrayBlockingQueue在创建时需要指定容量,且一旦设置就无法更改。当队列满时,后续任务将会被阻塞,直到队列中有空闲位置。
  3. 队列操作的性能:由于LinkedBlockingQueue采用链表实现,对于元素的插入和删除操作性能比较高。然而,对于随机访问元素的操作,由于需要遍历链表,性能比较低。而ArrayBlockingQueue采用数组实现,对于插入和删除操作性能也比较高。此外,由于数组支持随机访问,对于随机访问元素的操作性能也比较高。
  4. 阻塞操作的支持:LinkedBlockingQueue和ArrayBlockingQueue都支持阻塞操作。但是,它们的具体实现方式有所不同。LinkedBlockingQueue在插入和删除元素时,如果队列已满或为空,则会阻塞线程,直到队列有足够的空间或元素。而ArrayBlockingQueue在插入和删除元素时,如果队列已满或为空,则会立即阻塞线程,直到队列有足够的空间或元素。
  5. 内存占用:由于LinkedBlockingQueue采用链表实现,每个元素需要一个节点对象来保存。因此,如果队列中的元素比较多,LinkedBlockingQueue会占用更多的内存。而ArrayBlockingQueue采用数组实现,每个元素只需要一个数组元素来保存,因此内存占用相对较少。

总的来说,LinkedBlockingQueue适用于任务量不断增加的情况,可以无限制地添加任务,适合使用在不限制任务数量的场景。而ArrayBlockingQueue则适用于有固定容量限制的场景,可以确保队列的大小不会超过预设的容量。在选择使用哪种阻塞队列时,应根据具体的应用场景和需求进行权衡。

使用示例

LinkedBlockingQueueArrayBlockingQueue在Java编程中常常用于实现生产者-消费者模式,以及多线程之间的协作。以下是两种队列的使用示例:

ArrayBlockingQueue示例

import java.util.concurrent.ArrayBlockingQueue;public class ArrayBlockingQueueExample {public static void main(String[] args) throws InterruptedException {// 创建一个有界队列,容量为10ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);// 启动生产者线程Thread producer = new Thread(() -> {for (int i = 0; i < 20; i++) {try {System.out.println("生产者生产了: " + i);queue.put(i); // 将元素放入队列,如果队列满则阻塞Thread.sleep(1000); // 模拟生产耗时} catch (InterruptedException e) {e.printStackTrace();}}});// 启动消费者线程Thread consumer = new Thread(() -> {while (true) {try {Integer item = queue.take(); // 从队列中取出元素,如果队列空则阻塞System.out.println("消费者消费了: " + item);Thread.sleep(1500); // 模拟消费耗时} catch (InterruptedException e) {e.printStackTrace();}}});// 启动生产者和消费者线程producer.start();consumer.start();// 等待生产者线程完成producer.join();// 停止消费者线程(实际场景中可能需要根据实际情况来决定何时停止消费者线程)consumer.interrupt();}
}

LinkedBlockingQueue示例

import java.util.concurrent.LinkedBlockingQueue;public class LinkedBlockingQueueExample {public static void main(String[] args) throws InterruptedException {// 创建一个无界队列,或者可以指定一个容量LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();// 启动生产者线程Thread producer = new Thread(() -> {for (int i = 0; i < 20; i++) {try {System.out.println("生产者生产了: " + i);queue.put(i); // 将元素放入队列,如果队列满则阻塞Thread.sleep(1000); // 模拟生产耗时} catch (InterruptedException e) {e.printStackTrace();}}});// 启动消费者线程Thread consumer = new Thread(() -> {while (true) {try {Integer item = queue.take(); // 从队列中取出元素,如果队列空则阻塞System.out.println("消费者消费了: " + item);Thread.sleep(1500); // 模拟消费耗时} catch (InterruptedException e) {e.printStackTrace();}}});// 启动生产者和消费者线程producer.start();consumer.start();// 等待生产者线程完成producer.join();// 停止消费者线程(实际场景中可能需要根据实际情况来决定何时停止消费者线程)consumer.interrupt();}
}

在以上两个示例中,生产者和消费者线程通过puttake方法进行通信。当队列满时,生产者线程会阻塞,直到队列中有空位;当队列空时,消费者线程会阻塞,直到队列中有元素可取。这样,生产者和消费者就可以在并发环境下安全地协作。

请注意,在实际应用中,我们通常会使用更优雅的方式来停止消费者线程,而不是简单地调用interrupt()方法。例如,我们可以使用一个特殊的结束信号(如null或一个特殊的值)来表示队列中不再有新的元素,消费者线程在检测到这个信号后可以安全地结束。

这篇关于Java 中LinkedBlockingQueue和ArrayBlockingQueue的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Spring MVC如何设置响应

《SpringMVC如何设置响应》本文介绍了如何在Spring框架中设置响应,并通过不同的注解返回静态页面、HTML片段和JSON数据,此外,还讲解了如何设置响应的状态码和Header... 目录1. 返回静态页面1.1 Spring 默认扫描路径1.2 @RestController2. 返回 html2

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

SpringBoot 整合 Grizzly的过程

《SpringBoot整合Grizzly的过程》Grizzly是一个高性能的、异步的、非阻塞的HTTP服务器框架,它可以与SpringBoot一起提供比传统的Tomcat或Jet... 目录为什么选择 Grizzly?Spring Boot + Grizzly 整合的优势添加依赖自定义 Grizzly 作为

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

java Stream操作转换方法

《javaStream操作转换方法》文章总结了Java8中流(Stream)API的多种常用方法,包括创建流、过滤、遍历、分组、排序、去重、查找、匹配、转换、归约、打印日志、最大最小值、统计、连接、... 目录流创建1、list 转 map2、filter()过滤3、foreach遍历4、groupingB

SpringBoot如何使用TraceId日志链路追踪

《SpringBoot如何使用TraceId日志链路追踪》文章介绍了如何使用TraceId进行日志链路追踪,通过在日志中添加TraceId关键字,可以将同一次业务调用链上的日志串起来,本文通过实例代码... 目录项目场景:实现步骤1、pom.XML 依赖2、整合logback,打印日志,logback-sp