数据结构与算法之队列:队列在线程池等有限资源池中的应用

本文介绍了队列的基本概念,包括顺序队列、链式队列、循环队列、阻塞队列和并发队列的实现原理及应用场景。通过具体实例展示了不同类型的队列如何解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道,CPU资源时有限的,任务的处理速度与线程个数并不是线性相关。相反,过多的线程反而会导致CPU频繁切换,处理能力下降。所以线程池的大小一般都是综合考虑要在处理任务的特点和硬件环境,来事先设置的。

当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?时拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?

实际上,这些问题并不复杂,其底层的数据结构就是我们今天要学的内容,队列(queue)。

如何理解队列?

队列这个概念非常好理解。你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出,这就是典型的“队列”。

队列和栈非常相似,支持的操作也很有限,最基本的操作也是两个:入队出队
在这里插入图片描述
所以,队列和栈一样,也是一种操作受限的线性表数据结构。

队列的概念很好理解,基本操作也很容易掌握。作为一种非常基础的数据结构,队列的应用也非常广泛,特别是一些具有某些额外特性的队列,比如循环队列,阻塞队列,并发队列。它们在很多偏底层系统,框架,中间件的开发中,起着关键性的作用。比如高性能队列Disruptor,Linux环形缓存都用到了循环并发队列;Java concurrent并发包利用ArrayBlockingQueue来实现公平锁等。

顺序队列和链式队列

下面时简单的基于数组的顺序队列实现:

// 用数组实现的队列
public class ArrayQueue {
  // 数组:items,数组大小:n
  private String[] items;
  private int n = 0;
  // head 表示队头下标,tail 表示队尾下标
  private int head = 0;
  private int tail = 0;

  // 申请一个大小为 capacity 的数组
  public ArrayQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入队
  public boolean enqueue(String item) {
    // 如果 tail == n 表示队列已经满了
    if (tail == n) return false;
    items[tail] = item;
    ++tail;
    return true;
  }

  // 出队
  public String dequeue() {
    // 如果 head == tail 表示队列为空
    if (head == tail) return null;
    // 为了让其他语言的同学看的更加明确,把 -- 操作放到单独一行来写了
    String ret = items[head];
    ++head;
    return ret;
  }
}

基于链表的队列:
在这里插入图片描述

循环队列:
我们刚才用数组实现队列的时候,在tail==n时,会有数据搬移操作,这样入队操作性能就会受到影响。那有没有办法能够避免数据搬移呢?我们来看看循环队列的解决思路。

循环队列,顾名思义,它长得像一个环。原本数组时有头有尾的,时一条直线。现在我们把首尾相连,形成了一个环。如图所示:
在这里插入图片描述
循环队列的关键是确定好队空和队满的判定条件
在这里插入图片描述
就像如图所示的队满的情况:(tail + 1)%n=head

但是,循环队列会浪费一个数组的存储空间。

public class CircularQueue {
  // 数组:items,数组大小:n
  private String[] items;
  private int n = 0;
  // head 表示队头下标,tail 表示队尾下标
  private int head = 0;
  private int tail = 0;

  // 申请一个大小为 capacity 的数组
  public CircularQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入队
  public boolean enqueue(String item) {
    // 队列满了
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // 出队
  public String dequeue() {
    // 如果 head == tail 表示队列为空
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
  }
}

阻塞队列和并发队列

在这里插入图片描述
并发队列就是线程安全的队列。最简单直接的实现方式是直接在enqueue(),dequeue()方法上加上锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取。实际上,基于数组的循环队列,利用CAS原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值