什么是队列?
1.先进者先出,这就是典型的“队列”结构。
2.支持两个操作:入队enqueue(),放一个数据到队尾;出队dequeue(),从队头取一个元素。所以,和栈一样,队列也是一种操作受限的线性表。
队列的应用也非常广泛,特别是一些具有某些额外特性的队列,比如循环队列、阻塞队列、并发队列。它们在很多偏底层系统、框架、中间件的开发中,起着关键性的作用。
实现队列的两种方式?
顺序队列和链式队列
跟栈一样,队列可以用数组来实现,也可以用链表来实现。用数组实现的栈叫作顺序栈,用链表实现的栈叫作链式栈。同样,用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。
基于数组实现队列:
'''基于数组实现队列'''
class ArrayQueue:
def __init__(self,n):
self.items=[]
self.n=n #其中n为指定的数组容量
self.head=0#头指针
self.tail=0#尾指针
def enqueue(self,data):
if self.tail==self.n:
if self.head==0:
#队列已经满了
return False
else:
#需要做数据的搬移处理,因为此时数组前面还有空间
for i in range(0,self.tail-self.head):
self.items[i]=self.items[self.head+i]
self.tail=self.tail-self.head #重新指定尾指针的 位置
self.head=0#头指针重新指向首位
self.items.insert(self.tail,data)
self.tail=self.tail+1
return True
def dequeue(self):
if self.head!=self.tail:
data=self.items[self.head]#获取出队的值
self.head=self.head+1#头指针指向下个位置
return data
else:
return False
def __repr__(self):
return " ".join(i for i in self.items[self.head:self.tail])
print("----------")
queue=ArrayQueue(10)
for i in range(5):
queue.enqueue(str(i))
for i in range(6):
print(queue.dequeue())
print(queue)
基于链表实现队列:
'''基于链表实现队列'''
class Node():
def __init__(self,data,next=None):
self.data=data
self.next=next
class LinkedQueue():
def __init__(self):
#定义头指针和为指针,分别对应出队口和入队口
self.head=None
self.tail=None
#入队函数
def enqueue(self,data):
node= Node(data)
if self.tail:
self.tail.next=node #插入链表末尾
else:
self.head=node #如果此时链表为空,头指针指向当前插入节点
self.tail=node #让尾指针指向尾节点
return True
#出队函数
def dequeue(self):
if self.head:
data=self.head.data
self.head=self.head.next#头指针向后移动
if not self.head:
self.tail=None
return data #返回出队的值
else:
return False
def __repr__(self):
data=[]
current=self.head
while current:
data.append(current.data)
current=current.next
return "->".join(value for value in data)
if __name__=="__main__":
queue=LinkedQueue()
for i in range(5):
queue.enqueue(str(i))
print(queue)
print("----------")
for i in range(6):
print(queue.dequeue())
print(queue)
两者的区别
基于链表的实现方式,可以实现一个支持无限排队的无界队列,但是可能会导致过多的请求排队等待,请求处理的响应时间过长。所以,针对响应时间比较敏感的系统,基于链表实现的无限排队的线程池是不合适的。
而基于数组实现的有界队列,队列的大小有限,所以线程池中排队的请求超过队列大小时,接下来的请求就会被拒绝,这种方式对响应时间敏感的系统来说,就相对更加合理。不过,设置一个合理的队列大小,也是非常有讲究的。队列太大导致等待的请求太多,队列太小会导致无法充分利用系统资源、发挥最大性能。
队列有哪些常见的应用
1.阻塞队列
1)在队列的基础上增加阻塞操作,就成了阻塞队列。
2)阻塞队列就是在队列为空的时候,从队头取数据会被阻塞,因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后在返回。
2.并发队列
1)在多线程的情况下,会有多个线程同时操作队列,这时就会存在线程安全问题。能够有效解决线程安全问题的队列就称为并发队列。
2)并发队列简单的实现就是在enqueue()、dequeue()方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或取操作。
3)实际上,基于数组的循环队列利用CAS原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。
3.线程池资源枯竭时的处理
队列可以应用在任何有限资源池中,用于排队请求,比如数据库连接池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。
线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?各种处理策略又是如何实现的呢?
我们一般有两种处理策略。第一种是非阻塞的处理方式,直接拒绝任务请求;另一种是阻塞的处理方式,将请求排队,等到有空闲线程时,取出排队的请求继续处理。