LinkedListQueue
是 Java 中基于 LinkedList
实现的队列,支持 FIFO(先进先出)的数据结构。它允许在队列的头部和尾部高效地进行元素的插入和删除操作。由于 LinkedListQueue
继承自 LinkedList
,它具有动态数组的特性,能够根据需要自动调整大小。在多线程环境中,通常使用 LinkedBlockingQueue
作为线程安全的队列实现。
肖哥弹架构 跟大家“弹弹” 高并发锁, 关注公号回复 'mvcc' 获得手写数据库事务代码
欢迎 点赞,关注,评论。
关注公号Solomon肖哥弹架构获取更多精彩内容
历史热点文章
- 解锁大语言模型参数:零基础掌握大型语言模型参数奥秘与实践指南
- 高性能连接池之HikariCP框架分析:高性能逐条分解(架构师篇)
- 缓存雪崩/穿透/击穿/失效原理图/14种缓存数据特征+10种数据一致性方案
- Java 8函数式编程全攻略:43种函数式业务代码实战案例解析(收藏版)
- 一个项目代码讲清楚DO/PO/BO/AO/E/DTO/DAO/ POJO/VO
- 17个Mybatis Plugs注解:Mybatis Plugs插件架构设计与注解案例(必须收藏)
LinkedListQueue(业务结构)
LinkedListQueue 是一个基于链表实现的队列,它通常由一个双向链表构成,支持在队列的前端(头部)进行出队操作,以及在队列的后端(尾部)进行入队操作。
1、设计思考:
- 需求场景:
- 在许多编程任务中,需要一个能够快速进行插入和删除操作的队列结构。例如,在实现缓冲区管理、任务调度、消息队列等应用中,快速的入队和出队操作是非常重要的。
- 现有技术局限性:
- 传统的数组类型在 Java 中是固定长度的,一旦创建,其大小不能改变,这限制了其在需要动态大小管理时的使用。
Vector
类似于ArrayList
,但它是线程安全的,使用synchronized
进行同步,导致并发性能较差。ArrayDeque
提供了双端队列功能,但在某些情况下,可能需要进行数组的扩容或缩容操作。
- 技术融合:
LinkedListQueue
结合了LinkedList
的高效插入和删除特性,并提供了队列所需的先进先出(FIFO)的数据管理方式。它通过链表的双向链接特性,允许从两端快速地添加或移除元素,特别适合实现队列和双端队列。
- 设计理念:
LinkedListQueue
通过使用链表结构,可以有效地进行插入和删除操作,因为这些操作仅需要改变节点的指针,而不需要移动整个数组。它实现了Queue
接口,提供了队列所需的所有操作,包括offer
、poll
、peek
等。
- 实现方式:
LinkedListQueue
由一系列Node
对象组成,每个Node
包含数据和两个引用(previous
和next
),分别指向前一个和后一个节点。它提供了enqueue
(入队)和dequeue
(出队)操作,以及查看队列头部和尾部元素的方法,而不需要遍历整个链表。
2、数据结构
图说明:
- LinkedListQueue:
- 表示 LinkedListQueue 类的实例,是一个基于链表的队列数据结构。
- head:
- 表示队列的头节点,它指向队列中的第一个元素,用于出队操作。
- tail:
- 表示队列的尾节点,它指向队列中的最后一个元素,用于入队操作。
- Node:
- 链表中的节点,包含数据和两个引用,分别指向前一个节点和后一个节点。
- data:
- 节点中存储的数据。
- prev:
- 节点的前驱引用,指向前一个节点。
- next:
- 节点的后继引用,指向后一个节点。
3、执行流程
图说明:
- 开始:执行操作的起始点。
- 创建 LinkedListQueue 实例:初始化
LinkedListQueue
对象。 - 入队操作:执行将元素添加到队列尾部的操作。
- 添加元素到队尾:在队列的尾部添加新元素。
- 结束入队:完成入队操作。
- 出队操作:执行将元素从队列头部移除的操作。
- 移除队首元素:从队列头部移除元素。
- 结束出队:完成出队操作。
- 遍历队列:从头到尾遍历队列中的所有元素。
- 从头节点开始:从队列的头节点开始遍历。
- 访问每个节点:顺序访问链表中的每个节点。
- 读取节点数据:读取每个节点中存储的数据。
- 结束遍历:完成队列遍历。
- 结束:所有操作完成。
4、优点
- 高效的插入/删除操作:在队列的两端插入和删除元素的时间复杂度为 O(1)。
- 允许空队列:与某些数组实现的队列不同,
LinkedListQueue
允许空队列存在,不会因为空数组而抛出异常。 - 能够作为队列和双端队列使用:提供了实现队列和双端队列所需的方法。
5、缺点
- 随机访问性能差:由于链表的结构,随机访问元素的时间复杂度为 O(n)。
- 内存开销:相比于数组实现的队列,
LinkedListQueue
需要额外的内存来存储节点的指针。
6、使用场景
- 需要快速插入和删除元素的场景,如实现一个消息队列。
- 作为队列或双端队列使用,特别是在需要从两端进行元素添加或移除的操作中。
7、类设计
8、应用案例
在这个案例中,我们将使用 LinkedListQueue
来实现一个简单的打印任务队列。这个队列将按照先进先出的顺序处理任务。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
// 任务类,实现了Runnable接口,用于表示具体任务
class PrintTask implements Runnable {
private final String taskName;
public PrintTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
// 任务执行时间
try {
System.out.println("Task " + taskName + " is running");
Thread.sleep(2000); // 任务执行需要一些时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Task " + taskName + " was interrupted");
}
System.out.println("Task " + taskName + " finished");
}
}
public class LinkedListQueueExample {
public static void main(String[] args) {
// 创建一个LinkedListQueue实例,用作任务队列
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
// 添加任务到队列
taskQueue.offer(new PrintTask("Task 1"));
taskQueue.offer(new PrintTask("Task 2"));
taskQueue.offer(new PrintTask("Task 3"));
// 创建并启动一个新线程来处理队列中的任务
new Thread(() -> {
while (true) {
try {
// 从队列中取出一个任务并执行
taskQueue.take().run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Task processing was interrupted");
break;
}
}
}).start();
// 主线程继续执行其他任务或等待子线程完成
try {
Thread.sleep(6000); // 等待足够长的时间以确保所有任务都已完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Main thread was interrupted");
}
}
}