17.jdk源码阅读之LinkedBlockingQueue

news2024/9/20 8:41:37

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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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.coloradmin.cn/o/1945650.html

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

相关文章

RAS--APEI 报错解析流程(2)

RAS--APEI 报错解析流程(1) 除了APEI 中除了GHES会记录错误&#xff0c;在Post过程中的错误通常是通过BERT Table汇报 1.BERT Boot Error Record Table is used to report unhandled errors that occurred in a previous boot&#xff0c;it is reported as a ‘one-time polle…

一文弄懂JVM类加载器与双亲委派机制

类的加载器完成类的加载环节中的装载阶段的工作&#xff08;通过一个类的全限定名来获取该类的二进制字节流&#xff0c;且这个动作在虚拟机**外部实现**&#xff0c;即开发者可以决定如何去获取所需的类&#xff09;&#xff0c;且**不会影响后续的链接和初始化阶段&#xff0…

《算法笔记》总结No.10——链表

从第10期破例插叙一期单链表的实现&#xff0c;这个东东相当重要&#xff01;考研的同学也可以看&#xff1a;相较于王道考研的伪码不太相同&#xff0c;专注于可以运行。如果是笔试中的伪码&#xff0c;意思正确即可~ 注&#xff1a;博主之前写过一个版本的顺序表和单链表的C实…

谷粒商城实战笔记-56~57-商品服务-API-三级分类-修改-拖拽功能完成

文章目录 一&#xff0c;56-商品服务-API-三级分类-修改-拖拽功能完成二&#xff0c;57-商品服务-API-三级分类-修改-批量拖拽效果1&#xff0c;增加按钮2&#xff0c;多次拖拽一次保存完整代码 在构建商品服务API中的三级分类修改功能时&#xff0c;拖拽排序是一个直观且高效的…

Linux:Linux权限

目录 1. Linux权限的概念 2. Linux权限管理 2.1 文件访问者的分类 2.2 文件类型和访问权限 2.2.1 文件类型 2.2.2 基本权限 2.3 文件权限值的表示方法 2.4 文件访问权限的相关设置方法 2.4.1 chmod 2.4.2 chown 2.4.3 chgrp 2.4.4 umask 3. file指令 4. Linux目…

如何学习EMR:糙快猛的大数据之路(建立整体框架)

目录 初学EMREMR是什么&#xff1f;我的EMR学习故事糙快猛学习法则代码示例: 你的第一个EMR任务学习EMR的深入步骤EMR进阶技巧实用资源推荐常见挑战和解决方案 EMR生态EMR生态系统深度探索1. EMR上的Hadoop生态系统2. EMR Studio3. EMR on EKS 高级EMR配置和优化1. EMR实例集策…

音视频入门基础:PCM专题(3)——使用Audacity工具分析PCM音频文件

音视频入门基础&#xff1a;PCM专题系列文章&#xff1a; 音视频入门基础&#xff1a;PCM专题&#xff08;1&#xff09;——使用FFmpeg命令生成PCM音频文件并播放 音视频入门基础&#xff1a;PCM专题&#xff08;2&#xff09;——使用Qt播放PCM音频文件 音视频入门基础&am…

ICML 2024最佳论文开奖了!今年的热门投稿方向有这些

ICML 2024最近也放榜啦&#xff01;今年共有10篇论文夺得最佳论文奖&#xff0c;包括火爆的Stable Diffusion 3、谷歌VideoPoet以及世界模型Genie。 ICML是国际机器学习顶会&#xff0c;也是CCF-A类学术会议。今年这届顶会一共收到了9473篇论文&#xff0c;其中2610篇被录用&am…

昇思25天学习打卡营第22天|基于MindNLP+MusicGen生成自己的个性化音乐

文章目录 昇思MindSpore应用实践1、MusicGen模型简介残差矢量量化&#xff08;RVQ&#xff09;SoundStreamEncodec 2、生成音乐无提示生成文本提示生成音频提示生成 Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 1、MusicGen模型简…

Qt基础 | Qt SQL模块介绍 | Qt SQL模块常用类及其常用函数介绍

文章目录 一、Qt SQL模块概述1.Qt sql 支持的数据库2.SQLite 数据库3.Qt SQL 模块的主要类 一、Qt SQL模块概述 Qt SQL 模块提供数据库编程的支持&#xff0c;Qt 支持多种常见的数据库&#xff0c;如MySQL、Oracle、MS SQL Server、SQLite 等。Qt SQL 模块包括多个类&#xff0…

phpstorm配置xdebug3

查看php路径相关信息 php --ini安装xdebug https://www.jetbrains.com/help/phpstorm/2024.1/configuring-xdebug.html?php.debugging.xdebug.configure php.ini 配置 在最后添加&#xff0c;以下是我的配置 [xdebug] zend_extension/opt/homebrew/Cellar/php8.1/8.1.29/p…

安装NVIDIA驱动

一、不升级内核安装NVIDIA驱动 说明: 1、安装NVIDIA驱动,是用来提升AI、图片等算法 2、本人是在centos7.9操作系统安装英伟达T4板卡驱动 操作系统Centos 7.9驱动版本NVIDIA-Linux-x86_64-525.89.02.run操作账号root1.1 关闭nouveau 1、查看nouveau是否关闭 lsmod |grep nouv…

Android 常用调试工具/方法解析

一、内存相关 参考Android内存分析命令_dumpsys meminfo 算出rss-CSDN博客 1、基本概念 1&#xff09;PSS & RSS & USS & VSS a、PSS 概念&#xff1a;全称Proportional Set Size&#xff0c;根据进程实际使用的内存量按照共享比例分配给进程的一种内存度量方…

MySql性能调优05-[sql实战演练]

sql实战演练 行列转换行列式转换第一题【列转行】第二题【列转行】 having的使用找到表中&#xff0c;名字重复的项有数据表employee&#xff0c;包含如下字段id、name、department、age&#xff0c;编写SQL&#xff0c;找到不与其他人同龄的年纪最大的员工的年龄有数据表emplo…

Nacos-2.4.0最新版本docker镜像,本人亲自制作,部署十分方便,兼容postgresql最新版本17和16,奉献给大家了

基于Postgresql数据库存储的nacos最新版本2.4.0,采用docker镜像安装方式 因业务需要,为了让nacos支持postgresql,特意花了两天时间修改了源码,然后制作了docker镜像,如果你也在找支持postgresql的nacos最新版本,恭喜你,你来的正好~ nacos-2.4.0 postgresql的数据库脚本…

C++学习笔记-C++11中的智能指针

1.智能指针介绍 智能指针是C的特性用法&#xff0c;是一个类似指针功能的类对象&#xff0c;其目的是为了更好的管理动态分配的内存&#xff0c;避免出现内存泄漏、悬空指针等问题。C11的标准库里提供了三种智能指针模板类&#xff0c;分别是std::unique_ptr、std::shared_ptr…

vue 两个页面切换, 再回到当前页,还是离开前的数据

1、要保证页面的name 和 建路由的大小写一致 2、页面不用生命周期--activated 调接口刷新

计算机网络八股文(三)

目录 41.为什么每次建立TCP连接时&#xff0c;初始化的序列号都不一样&#xff1f; 42.初始序列号ISN如何随机产生&#xff1f; 43.既然IP层会分片&#xff0c;为什么TCP层需要根据MSS分片呢&#xff1f; 44.TCP第一次握手丢失&#xff0c;会发生什么&#xff1f; 45.TCP第…

一个python脚本解决新版剪映导出字幕收费问题

如果你是希望我能完全解决剪映收费问题&#xff0c;我无法帮你&#xff1b; 两个文件&#xff0c;可生成不带时间线的纯文案&#xff0c;MD 格式&#xff0c;也可以生成带时间线的 SRT 文件。 因为剪映国内版对 JSON 文件进行了加密&#xff0c;所以请选择国际版 Cutcap&#x…

《javaEE篇》--阻塞队列详解

阻塞队列 阻塞队列概述 阻塞队列也是一种队列&#xff0c;和普通队列一样遵循先进先出的原则&#xff0c;但是阻塞队列相较于普通队列多了两项功能阻塞添加和阻塞移除&#xff0c;使得阻塞队列成为一种线程安全的数据结构 阻塞添加&#xff1a;当队列满的时候继续入队就会阻…