对列的相关概念
- 先进先出,即队列
- 队列的两个基本操作:入队(放一个数据到队列尾部)和出队(从队列头部取一个元素)
- 队列是一种操作受限的线性表数据结构
顺序队列和链式队列
-
用数组实现的队列叫作顺序队列
class Queue: """ 一个简单的队列实现 属性: items(list): 队列数组 """ def __init__(self): """ Queue类的构造函数 参数: 无 返回值: 无 """ self.items = [] def is_empty(self): """ 检查队列是否为空 参数: 无 返回值: True 或 False """ return len(self.items) == 0 def enqueue(self, item): """ 将一个元素添加到队列中 参数: 无 返回值: 添加的元素 """ self.items.append(item) def dequeue(self): """ 将一个元素从队列中移除 参数: 无 返回值: 删除的元素值 """ if self.is_empty(): raise Exception("Queue is empty") return self.items.pop(0)
-
用链表实现的队列叫作链式队列
class Node: """ 初始化队列中的每个节点。 属性: val (int): 表示节点值。 next (int): 表示下一个节点的指针。 """ def __init__(self, val): """ 节点构造函数。 参数: val: 节点的值。 返回: 无。 """ # val 节点的值 # next 表示下一个节点的指针 self.val = val self.next = None class Queue: """ 实现队列操作。 属性: head (int): 表示队列头部索引的整数。 tail (int): 表示队列尾部索引的整数。 """ def __init__(self): """ 队列构造函数。 参数: 无。 返回: 无。 """ # head 是队列头部节点 self.head = None # tail 是队列尾部节点 self.tail = None def enqueue(self, val): """ 将新元素添加到队列尾部。 参数: val: 要添加到队列尾部的值。 返回: 无。 """ new_node = Node(val) if not self.head: self.head = new_node self.tail = new_node else: self.tail.next = new_node self.tail = new_node def dequeue(self): """ 从队列头部删除元素并返回其值。 参数: 无。 返回: 删除的元素值,如果队列为空则返回 None。 """ # 检查队列是否为空 if not self.head: return None val = self.head.val self.head = self.head.next # 再次判断,确保队列状态正确 if not self.head: self.tail = None return val def is_empty(self): """ 判断队列是否为空。 参数: 无。 返回: 如果队列为空则返回 True,否则返回 False。 """ return self.head is None
线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?各种处理策略又是如何实现的呢?
-
非阻塞的处理方式,直接拒绝任务请求;
-
阻塞的处理方式,将请求排队,等到有空闲线程时,取出排队的请求继续处理
-
使用队列(数组实现 or 链表实现)存储排队请求
1)基于链表的实现方式,可以实现一个支持无限排队的无界队列(unbounded queue),但是可能会导致过多的请求排队等待,请求处理的响应时间过长。所以,针对响应时间比较敏感的系统,基于链表实现的无限排队的线程池是不合适的。
2)基于数组实现的有界队列(bounded queue),队列的大小有限,所以线程池中排队的请求超过队列大小时,接下来的请求就会被拒绝,这种方式对响应时间敏感的系统来说,就相对更加合理。不过,设置一个合理的队列大小,也是非常有讲究的。队列太大导致等待的请求太多,队列太小会导致无法充分利用系统资源、发挥最大性能。
-
对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队
循环队列
- 关键点:确定好队空和队满的判定条件
- 队空判断条件:head == tail
- 队满判断条件:(tail + 1) % n == head
class CircularQueue:
"""
使用列表实现的循环队列。
属性:
queue (list): 用于存储队列元素的列表。
head (int): 表示队列头部索引的整数。
tail (int): 表示队列尾部索引的整数。
maxSize (int): 表示队列的最大大小的整数。
"""
def __init__(self):
"""
CircularQueue类的构造函数。
参数:
无。
返回:
无。
"""
self.queue = []
self.head = 0
self.tail = 0
self.maxSize = 8
def enqueue(self, data):
"""
将一个元素添加到队列中。
参数:
data: 要添加到队列中的数据。
返回:
如果元素成功添加,则返回True;如果队列已满,则返回"Queue Full!"。
"""
# 判断队列是否已满。
if self.size() == self.maxSize - 1:
return "Queue Full!"
self.queue.append(data)
self.tail = (self.tail + 1) % self.maxSize
return True
def dequeue(self):
"""
从队列中移除一个元素。
参数:
无。
返回:
如果队列不为空,则返回从队列中移除的元素;如果队列为空,则返回"Queue Empty!"。
"""
if self.size() == 0:
return "Queue Empty!"
data = self.queue[self.head]
self.head = (self.head + 1) % self.maxSize
return data
def size(self):
"""
计算队列的大小。
参数:
无。
返回:
队列的大小。
"""
if self.tail >= self.head:
return self.tail - self.head
return self.maxSize - (self.head - self.tail)
阻塞队列
阻塞队列其实就是在队列基础上增加了阻塞操作。当队列已满时,插入操作将被阻塞,直到有空闲的位置;当队列为空时,删除操作将被阻塞,直到队列中有元素可供删除。阻塞队列通常用于多线程场景下的任务调度和协作,可以很好地控制线程的执行顺序和并发度,避免了线程之间的竞争和冲突
-
使用阻塞队列可以实现一个”生产者 - 消费者模型“
-
基于阻塞队列,我们还可以通过协调“生产者”和“消费者”的个数,来提高数据的处理效率
并发队列
并发队列是一种支持多线程并发访问的队列,它可以在多个线程同时对队列进行插入、删除等操作,而不会出现数据不一致或并发冲突的情况
最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。
- 实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列