17.jdk源码阅读之LinkedBlockingQueue

1. 写在前面

LinkedBlockingQueue 是 Java 并发包中的一个重要类,常用于生产者-消费者模式等多线程编程场景。上篇文章我们介绍了ArrayBlockingQueue,并且与LinkedBlockingQueue做了简单的对比,这篇文章我们来详细分析下LinkedBlockingQueue的源码,在此之前,我先抛出几个问题,看看大家之前有没有思考过:

  1. LinkedBlockingQueue 如何实现线程安全?
  2. LinkedBlockingQueue 的插入和移除操作是如何实现的?
  3. LinkedBlockingQueue 的 size() 方法是线程安全的吗?
  4. LinkedBlockingQueue 适用于哪些场景?
  5. 如何使用 LinkedBlockingQueue 实现一个简单的生产者-消费者模型?
  6. LinkedBlockingQueue 的性能如何?如何优化?
  7. 如何处理 LinkedBlockingQueue 中的阻塞操作?
  8. LinkedBlockingQueue 如何实现序列化?

2. 全局视角

在这里插入图片描述
LinkedBlockingQueue 直接继承自 AbstractQueue,而 AbstractQueue 又继承自 AbstractCollection。
LinkedBlockingQueue 实现了以下接口:

  • BlockingQueue
  • Serializable

2.1 AbstractCollection

AbstractCollection 是 Java 集合框架中的一个抽象类,它实现了 Collection 接口的大部分方法,但不包括 size() 和 iterator() 方法。LinkedBlockingQueue 通过继承 AbstractCollection,获得了 Collection 接口的大部分实现。

2.2 AbstractQueue

AbstractQueue 继承自 AbstractCollection,并实现了 Queue 接口的一部分方法。它提供了 offer()、poll() 和 peek() 方法的一些默认实现。LinkedBlockingQueue 通过继承 AbstractQueue,获得了 Queue 接口的一部分实现。

2.3 BlockingQueue

BlockingQueue 是 Java 并发包中的一个接口,它扩展了 Queue 接口,并增加了阻塞操作的方法,如 put(E e)、take()、offer(E e, long timeout, TimeUnit unit) 和 poll(long timeout, TimeUnit unit)。LinkedBlockingQueue 通过实现 BlockingQueue 接口,提供了这些阻塞操作的方法。

2.4 Serializable

Serializable 是一个标记接口,表示该类的实例可以被序列化。LinkedBlockingQueue 实现了 Serializable 接口,使其实例可以被序列化和反序列化。

3. 从使用说起

以下是一个完整的示例代码,展示了如何使用 LinkedBlockingQueue 实现生产者-消费者模式:

import java.util.concurrent.LinkedBlockingQueue;// 生产者类
class Producer implements Runnable {private LinkedBlockingQueue<Integer> queue;public Producer(LinkedBlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("Producing: " + i);queue.put(i); // 阻塞直到队列有空间Thread.sleep(500); // 模拟生产时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}// 消费者类
class Consumer implements Runnable {private LinkedBlockingQueue<Integer> queue;public Consumer(LinkedBlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {while (true) {Integer item = queue.take(); // 阻塞直到队列有元素System.out.println("Consuming: " + item);Thread.sleep(1000); // 模拟消费时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}// 主类
public class ProducerConsumerExample {public static void main(String[] args) {LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5); // 创建一个容量为5的有界队列Thread producerThread = new Thread(new Producer(queue));Thread consumerThread = new Thread(new Consumer(queue));producerThread.start();consumerThread.start();}
}

3.1 创建 LinkedBlockingQueue 实例

LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5); // 创建一个容量为5的有界队列

这里创建了一个容量为 5 的 LinkedBlockingQueue 实例。这个队列将用于生产者和消费者之间的通信。

3.2 生产者类 Producer

class Producer implements Runnable {private LinkedBlockingQueue<Integer> queue;public Producer(LinkedBlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("Producing: " + i);queue.put(i); // 阻塞直到队列有空间Thread.sleep(500); // 模拟生产时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
  • Producer 类实现了 Runnable 接口。
  • 在 run 方法中,生产者在循环中生产 10 个整数。
  • 调用 queue.put(i) 将元素放入队列,如果队列已满,put 方法会阻塞直到有空间。
  • Thread.sleep(500) 用于模拟生产时间。

3.3 消费者类 Consumer

class Consumer implements Runnable {private LinkedBlockingQueue<Integer> queue;public Consumer(LinkedBlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {while (true) {Integer item = queue.take(); // 阻塞直到队列有元素System.out.println("Consuming: " + item);Thread.sleep(1000); // 模拟消费时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
  • Consumer 类也实现了 Runnable 接口。
  • 在 run 方法中,消费者在一个无限循环中消费队列中的元素。
  • 调用 queue.take() 从队列中取出元素,如果队列为空,take 方法会阻塞直到有元素。
  • Thread.sleep(1000) 用于模拟消费时间。

3.4 主类 ProducerConsumerExample

public class ProducerConsumerExample {public static void main(String[] args) {LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5); // 创建一个容量为5的有界队列Thread producerThread = new Thread(new Producer(queue));Thread consumerThread = new Thread(new Consumer(queue));producerThread.start();consumerThread.start();}
}
  • 创建 LinkedBlockingQueue 实例。
  • 创建生产者和消费者线程,并启动它们。

3.5 运行结果

运行上述代码后,你会看到生产者和消费者在控制台中输出的日志,展示了生产和消费的过程:

Producing: 0
Consuming: 0
Producing: 1
Producing: 2
Consuming: 1
Producing: 3
Producing: 4
Consuming: 2
...

4. LinkedBlockingQueue 如何实现线程安全?

LinkedBlockingQueue 使用两个独立的锁(putLock 和 takeLock)来分别控制插入和移除操作。它还使用条件对象(notEmpty 和 notFull)来管理队列的空和满状态,从而实现阻塞操作。

5. LinkedBlockingQueue 的插入和移除操作是如何实现的?

  • 插入操作(put):获取 putLock,如果队列已满,调用 notFull.await() 阻塞等待。插入元素后,更新元素数量并释放 putLock。如果队列之前为空,调用 signalNotEmpty() 唤醒等待的消费者。
  • 移除操作(take):获取 takeLock,如果队列为空,调用 notEmpty.await() 阻塞等待。移除元素后,更新元素数量并释放 takeLock。如果队列之前已满,调用 signalNotFull() 唤醒等待的生产者。

6. LinkedBlockingQueue 的 size() 方法是线程安全的吗?

size() 方法是线程安全的,因为它使用了一个 AtomicInteger 类型的 count 来跟踪队列中的元素数量。AtomicInteger 提供了原子操作,确保了线程安全性。

7. LinkedBlockingQueue 适用于哪些场景?

  • 生产者-消费者模型:多个生产者线程和多个消费者线程之间的任务调度。
  • 任务队列:在多线程环境下,用于存储和调度待处理任务。
  • 异步处理:在异步处理场景中,用于存储和处理异步任务。

8. LinkedBlockingQueue 的性能如何?如何优化?

  • LinkedBlockingQueue 在高并发环境下性能较好,因为它使用了独立的锁来并行处理插入和移除操作。
  • 优化可以从以下几个方面考虑:
    • 调整队列的容量,以适应具体的应用场景。
    • 使用批量操作(如 drainTo 方法)来减少锁竞争。
    • 在高并发场景下,可以考虑使用其他高性能队列(如 ConcurrentLinkedQueue)来替代。

9. 如何处理 LinkedBlockingQueue 中的阻塞操作?

  • 可以使用带超时的阻塞方法(如 offer(E e, long timeout, TimeUnit unit) 和 poll(long timeout, TimeUnit unit))来避免永久阻塞。
  • 在某些情况下,可以使用非阻塞方法(如 offer(E e) 和 poll())来检查队列状态并采取相应的措施。

10. LinkedBlockingQueue 如何实现序列化?

  • LinkedBlockingQueue 实现了 Serializable 接口,因此它的实例可以被序列化和反序列化。
  • 序列化时,会将队列中的元素和容量信息序列化。
  • 反序列化时,会重新构造队列,并将元素添加到新的队列实例中。

系列文章

1.JDK源码阅读之环境搭建

2.JDK源码阅读之目录介绍

3.jdk源码阅读之ArrayList(上)

4.jdk源码阅读之ArrayList(下)

5.jdk源码阅读之HashMap

6.jdk源码阅读之HashMap(下)

7.jdk源码阅读之ConcurrentHashMap(上)

8.jdk源码阅读之ConcurrentHashMap(下)

9.jdk源码阅读之ThreadLocal

10.jdk源码阅读之ReentrantLock

11.jdk源码阅读之CountDownLatch

12.jdk源码阅读之CyclicBarrier

13.jdk源码阅读之Semaphore

14.jdk源码阅读之线程池(上)

15.jdk源码阅读之线程池(下)

16.jdk源码阅读之ArrayBlockingQueue

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

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

相关文章

从零开始构建你的第一个Python Web应用

在本文中&#xff0c;我们将带领你从零开始构建一个简单的Python Web应用。不需要任何先验知识&#xff0c;我们会一步步地指导你完成设置、框架选择、代码编写到部署的整个过程。无论你是Web开发新手还是希望扩展技能的老手&#xff0c;这篇文章都将为你提供一个实践操作的起点…

Spring-Aop源码解析(二)

书接上文&#xff0c;上文说到&#xff0c;specificInterceptors 不为空则执行createProxy方法创建代理对象&#xff0c;即下图的createProxy方法开始执行&#xff0c;生成代理对象&#xff0c;生成代理对象有两种方式&#xff0c;JDK和CGLIB。 createAopProxy就是决定使用哪…

【数据结构 | 哈希表】一文了解哈希表(散列表)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

昇思学习打卡-22-生成式/DCGAN生成漫画头像

文章目录 DCGAN网络数据处理构造网络生成器判别器损失函数优化器 结果展示 我们将学习DCGAN网络如何数据处理、设置网络&#xff0c;包括生成器、判别器、损失函数、优化器等。 DCGAN网络 DCGAN&#xff08;深度卷积对抗生成网络&#xff0c;Deep Convolutional Generative Ad…

windows下运行sh文件

1、打开git bash 2、进入sh文件所在文件夹&#xff0c;使用sh xx.sh运行

普发Pfeiffer TPG300手侧配置安装操作技术资疗包含

普发Pfeiffer TPG300手侧配置安装操作技术资疗包含

学习笔记:MySQL数据库操作2

1. 建库建表 创建数据库 mydb8_worker。使用该数据库 mydb8_worker。创建职工表 t_worker&#xff0c;字段包括&#xff1a; department_id: 部门号&#xff0c;整型&#xff0c;不允许为空。worker_id: 职工号&#xff0c;主键&#xff0c;整型&#xff0c;不允许为空。worke…

硬盘数据恢复的基本原理是什么 硬盘数据恢复教程

无论是电脑硬盘&#xff0c;还是日常办公过程中使用系统硬盘&#xff0c;都是由多个存储空间组成的。如果这些存储空间中的信息被删除了&#xff0c;那内部的文件也会跟着消失。下面&#xff0c;小编就以“硬盘数据恢复工具恢复原理&#xff0c;硬盘数据恢复教程”这两个问题为…

昇思25天学习打卡营第18天 | DCGAN生成漫画头像

探索DCGAN在生成动漫头像的实用性 通过深入学习和实践DCGAN&#xff08;Deep Convolutional Generative Adversarial Networks&#xff09;&#xff0c;我对这种深度学习模型在生成动漫头像方面的应用有了更全面的理解。DCGAN作为一种改进的GAN模型&#xff0c;通过在生成器和…

MP的使用

1、MP简介 MyBatis-Plus&#xff08;简称MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生 官网&#xff1a;MyBatis-Plus &#x1f680; 为简化开发而生 参考教程&#xff1a;https://baomidou.c…

土地规划中的环境影响评估:守护绿水青山的科学指南

土地规划&#xff0c;作为指导区域开发与保护的蓝图&#xff0c;其决策不仅关乎经济发展&#xff0c;更与生态环境息息相关。环境影响评估&#xff08;EIA&#xff09;作为土地规划不可或缺的一环&#xff0c;旨在预测、评估规划项目对自然环境和社会环境的潜在影响&#xff0c…

英语科技写作 希拉里·格拉斯曼-蒂(英文版)pdf下载

下载链接&#xff1a; 链接1&#xff1a;https://pan.baidu.com 链接2&#xff1a;/s/1fxRUGnlJrKEzQVF6k1GmBA 提取码&#xff1a;b69t 由于是英文版&#xff0c;可能有些看着不太方便&#xff0c;可以在网页版使用以下软件中英文对照着看&#xff0c;看着更舒服&#xff0c;…

【echarts区域地图】

背景&#xff1a;我们在制作大屏的时候&#xff0c;经常会使用到echarts制作各种图表&#xff0c;饼图&#xff0c;柱状图&#xff0c;折线图。有时候也会用到地图的交互&#xff0c;使大屏效果看起来更加高级。 我们要完成上面的效果需要准备什么呢&#xff1f; 首先是需要我…

Gson的基本使用:解析Json格式数据 序列化与反序列化

目录 一&#xff0c;Gson和Json 1&#xff0c;Gson 2&#xff0c;Json 3&#xff0c;Gson处理对象的几个重要点 4&#xff0c;序列化和反序列化 二&#xff0c;Gson的使用 1&#xff0c;Gson的创建 2&#xff0c;简单对象序列化 3&#xff0c;对象序列化&#xff0c;格…

基于ansible进行运维自动化的研究以及相关的属性

一、ansible-简介 介绍 ansible是新出现的自动化运维工具&#xff0c;基于Python开发&#xff0c;集合了众多运维工具&#xff08;puppet、cfengine、chef、func、fabric&#xff09;的优点&#xff0c; 实现了批量系统配置、批量程序部署、批量运行命令等功能。 无客户端。 …

【LeetCode】71.简化路径

1. 题目 2. 分析 3. 代码 我写了一版很复杂的代码&#xff1a; class Solution:def simplifyPath(self, path: str) -> str:operator [] # 操作符的栈dir_name [] # 文件名的栈idx 0cur_dir_name ""while(idx < len(path)):if path[idx] /:operator.ap…

最新可用度盘不限速后台系统源码_去授权开心版

某宝同款度盘不限速后台系统源码&#xff0c;验证已被我去除&#xff0c;两个后端系统&#xff0c;账号和卡密系统 第一步安装宝塔&#xff0c;部署卡密系统&#xff0c;需要环境php7.4 把源码丢进去&#xff0c;设置php7.4&#xff0c;和伪静态为thinkphp直接访问安装就行 …

音频处理过程

1、音频 &#xff08;1&#xff09;打开设备 &#xff08;2&#xff09;从音频设备中读取数据 &#xff08;3&#xff09;将音频设备中读取的数据写入文件夹中 &#xff08;4&#xff09; 通过界面控制开始录制和结束录制&#xff08;使用多线程和状态码控制&#xff09; &…

C++与C中,由函数形参test(int *a)引出的问题

文章参考来源&#xff1a; 1.c函数中形参为引用的情况&#xff1b;C中a和&a的区别 描述&#xff1a; 最近在看循环单链表时&#xff0c;看到有篇文章中&#xff0c;链表初始化函数为图下&#xff0c;我在想&#xff0c;这个函数形参(类似 "int * & a"一样)到…

【C# WInForm】将TextBox从输入框设置为文本框

1.需求情形&#xff1a; textbox作为最常用的控件之一&#xff0c;通常是用来输入文本信息或者显示文字&#xff0c;但是如果要在界面中显示大段文本&#xff0c;一个带有边框、可选中的文本样式似乎不合适。像这样&#xff1a; 我需要的是这段文字不仅能跨行&#xff0c;而且…