简单易懂的Java Queue入门教程!

哈喽,各位小伙伴们,你们好呀,我是喵手。

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

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

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

如下是Java集合体系架构图,近期几期内容都是围绕该体系进行知识讲解,以便于同学们学习Java集合篇知识能够系统化而不零散。

前言

在软件开发领域,队列是一种非常重要的数据结构。它被广泛应用于多线程、网络通信、缓存、消息队列等场景。Java语言提供了多种队列实现,其中最常用的是Queue接口及其实现类。下面将详细介绍Java中的Queue及其应用场景。

摘要

本文将介绍Java中Queue接口及其实现类的概念、源代码解析、应用场景案例、优缺点分析、类代码方法介绍、测试用例等内容。原则上,读者需要掌握Java基础语法及数据结构相关知识。

Queue

简介

Queue是Java中的一种接口,它继承自Collection接口,并添加了一些队列相关的方法。Queue的实现类有多种,包括LinkedList、ArrayBlockingQueue、PriorityQueue、ConcurrentLinkedQueue等。下面简要介绍一下各个实现类的特点:

  1. LinkedList

LinkedList是Java中的一个双向链表实现类。LinkedList实现了Deque接口,Deque又是Queue接口的子接口,因此LinkedList可以被视为Queue的一种实现。LinkedList的优点是插入、删除操作效率高,但是随机访问效率低。

  1. ArrayBlockingQueue

ArrayBlockingQueue是Java中的一个基于数组的有界阻塞队列实现类。它的内部实现类似于一个环形数组,数组的长度是固定的。ArrayBlockingQueue具有先进先出的队列特性,它支持多线程安全访问,可以指定等待时间,当队列满或空时,线程将被阻塞。

  1. PriorityQueue

PriorityQueue是Java中的一个带优先级的队列实现类。它的内部是一个堆结构,根据元素的排序规则进行排序,每次出队时都会返回当前队列中最小(或最大)的元素。PriorityQueue的特点是插入、删除效率高,但是元素数量较大时遍历效率不高。

  1. ConcurrentLinkedQueue

ConcurrentLinkedQueue是Java中的一个基于链表的无界并发队列实现类。ConcurrentLinkedQueue的特点是多线程安全,能够高效地处理高并发场景。它不支持阻塞操作,而且不保证元素的排序。

源代码解析

下面我们以ArrayBlockingQueue为例,简单解析一下它的源代码。

java 复制代码
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    /** The queued items */
    final Object[] items;
    /** items index for next take, poll or remove */
    int takeIndex;
    /** items index for next put, offer, or add */
    int putIndex;
    /** Number of elements in the queue */
    int count;
    /** Main lock guarding all access */
    final ReentrantLock lock;
    /** Condition for waiting takes */
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;
    // ...
}

如上所示,ArrayBlockingQueue继承了AbstractQueue类,并实现了BlockingQueue接口和Serializable接口。其内部使用了一个Object数组items用于存储队列中的元素,使用takeIndex和putIndex分别表示下一次取出元素和添加元素的位置,使用count表示队列中当前元素个数,使用lock实现线程同步,使用notEmpty和notFull两个Condition实现阻塞和唤醒操作。

如下是部分源码截图:

应用场景案例

下面介绍一下Queue在实际开发中的应用场景:

  1. 消息队列

    在分布式系统中,消息队列是一种常用的通信方式。生产者向队列中添加消息,消费者从队列中取出消息进行处理。Java语言中提供的ActiveMQ和RabbitMQ等消息队列服务均使用了队列的相关技术。

  2. 线程池

    在Java中,线程池通常使用BlockingQueue来存储待执行的任务。当线程池中的线程已经全部分配任务时,新的任务将会被阻塞,直到有线程可用为止。Java中的ThreadPoolExecutor就是使用了BlockingQueue来存储待执行的任务。

  3. 缓存

在Java中,缓存通常使用LinkedList或ConcurrentLinkedQueue来实现。缓存存储的是最近访问过的元素,当缓存已满时,新的元素会将最旧的元素移除。

优缺点分析

Queue的优缺点分析如下:

  1. 优点

    Queue能够高效地实现元素的插入、删除以及元素的存储。Queue还能够在多线程场景中安全地实现元素的读写操作。

  2. 缺点

在某些场景下,Queue可能会存在性能瓶颈。例如当Queue中元素数量较大时,遍历操作效率较低,此时需要使用其他数据结构来优化性能。另外Queue中元素的顺序是固定的,无法对元素的顺序进行修改。

类代码方法介绍

Queue接口中包含了很多常用的方法,下面介绍一下其中一些常用的方法:

  1. add(E e)

    将元素e添加到队列尾部,如果队列已满,则抛出IllegalStateException异常。

  2. offer(E e)

    将元素e添加到队列尾部,返回值表示添加操作是否成功。

  3. remove()

    移除队列头部的元素并返回该元素,如果队列为空,则抛出NoSuchElementException异常。

  4. poll()

    移除队列头部的元素并返回该元素,如果队列为空,则返回null。

  5. element()

    返回队列头部的元素,如果队列为空,则抛出NoSuchElementException异常。

  6. peek()

    返回队列头部的元素,如果队列为空,则返回null。

测试用例

下面给出ArrayBlockingQueue的测试用例:

测试代码

java 复制代码
package com.example.javase.collection;

import java.util.concurrent.*;

/**
 * @Author ms
 * @Date 2023-10-24 23:32
 */
public class QueueTest {

    private static final int BUFFER_SIZE = 10;
    private static final int PRODUCER_COUNT = 5;
    private static final int CONSUMER_COUNT = 5;
    private static final int ITEM_COUNT = 1000;

    private static final ExecutorService pool = Executors.newCachedThreadPool();

    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(BUFFER_SIZE);

        for (int i = 0; i < PRODUCER_COUNT; i++) {
            pool.execute(new Producer(queue, ITEM_COUNT));
        }

        for (int i = 0; i < CONSUMER_COUNT; i++) {
            pool.execute(new Consumer(queue, ITEM_COUNT));
        }

        TimeUnit.SECONDS.sleep(60);

        pool.shutdown();
    }

    static class Producer implements Runnable {
        final BlockingQueue<Integer> queue;
        final int count;

        Producer(BlockingQueue<Integer> queue, int count) {
            this.queue = queue;
            this.count = count;
        }

        public void run() {
            try {
                for (int i = 0; i < count; i++) {
                    queue.put(i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    static class Consumer implements Runnable {
        final BlockingQueue<Integer> queue;
        final int count;

        Consumer(BlockingQueue<Integer> queue, int count) {
            this.queue = queue;
            this.count = count;
        }

        public void run() {
            try {
                for (int i = 0; i < count; i++) {
                    queue.take();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

这段代码实现了生产者-消费者模型,使用了Java内置的阻塞队列ArrayBlockingQueue来作为共享的缓冲区。主要包含了三个部分:

  1. 定义了共享缓冲区的大小BUFFER_SIZE、生产者和消费者的数量PRODUCER_COUNT和CONSUMER_COUNT,以及需要生产和消费的数据ITEM_COUNT。

  2. 在main方法中,创建一个ArrayBlockingQueue实例,并分别创建PRODUCER_COUNT个生产者线程和CONSUMER_COUNT个消费者线程,然后分别启动这些线程并等待60秒钟后关闭线程池。

  3. Producer和Consumer类都实现了Runnable接口。在Producer的run方法中,不断向队列中添加数据,直到添加的次数达到ITEM_COUNT;在Consumer的run方法中,不断从队列中取出数据,直到取出的次数达到ITEM_COUNT。由于ArrayBlockingQueue是线程安全的,因此不需要做额外的同步处理。

总的来说,这段代码展示了如何使用Java内置的阻塞队列来实现生产者-消费者模型,避免了手动实现同步和互斥的复杂性,提高了代码的可读性、可维护性和可复用性。

测试结果

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

代码分析

根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。   该代码是一个使用 ArrayBlockingQueue 实现的生产者-消费者模型的示例。包含一个主类 ArrayBlockingQueueTest 和两个内部静态类 Producer 和 Consumer。主类中创建一个 ArrayBlockingQueue,然后创建 PRODUCER_COUNTProducer 线程和 CONSUMER_COUNTConsumer 线程,并启动它们。每个 Producer 线程向队列中插入 ITEM_COUNT 个元素,每个 Consumer 线程从队列中取出 ITEM_COUNT 个元素。当所有线程运行完毕后,程序将等待 60 秒钟,然后关闭线程池。

ProducerConsumer 类实现了 Runnable 接口,分别包含一个 BlockingQueue 对象和一个 count 变量。在 run() 方法中,Producer 线程将元素插入队列中,Consumer 线程从队列中取出元素。如果队列已满(对于 Producer)或者队列为空(对于 Consumer),线程会进入阻塞状态。如果线程被中断,则中断状态会被设置并退出线程。

该示例使用了线程池来管理并发任务,增加效率。使用 ArrayBlockingQueue 实现了并发访问控制,保证了线程安全。

小结

Queue是Java中的一个接口,它的实现类包括LinkedListArrayBlockingQueuePriorityQueueConcurrentLinkedQueue等。Queue常用于消息队列、线程池、缓存等场景。在使用Queue时需要注意线程安全性和性能问题。

总结

本文介绍了Java中Queue接口及其实现类的概念、源代码解析、应用场景案例、优缺点分析、类代码方法介绍、测试用例等内容。Queue是Java中常用的数据结构,能够高效地实现元素的插入、删除及存储,在多线程场景中也能保障线程安全。常用的Queue实现类有LinkedList、ArrayBlockingQueue、PriorityQueue、ConcurrentLinkedQueue等,它们都具有各自的优缺点。Queue的应用场景包括消息队列、线程池、缓存等。在使用Queue时需要注意性能问题和线程安全性。

... ...

文末

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

... ...

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

wished for you successed !!!


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

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

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

相关推荐
一只叫煤球的猫4 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9654 小时前
tcp/ip 中的多路复用
后端
bobz9654 小时前
tls ingress 简单记录
后端
皮皮林5516 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友6 小时前
什么是OpenSSL
后端·安全·程序员
bobz9656 小时前
mcp 直接操作浏览器
后端
前端小张同学8 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook8 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康9 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在9 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net