队列是什么(顺序队列、链式队列、循环队列、阻塞队列、并发队列)

一、了解线程池

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

另外,当线程池没有空闲线程时,新的任务请求线程资源时,我们一般有两种处理策略,第一种是非阻塞的处理方式,直接拒绝任务请求;另一种是阻塞的处理方式,将请求排队,等到有空闲线程时,去除排队的请求继续处理,那么如何处理排队的请求呢,可以用队列来存储,有两种实现方式:第一种基于链表的实现,可以实现一个支持无限排队的无界队列,但是可能导致过多的请求排队等待,请求处理的响应时间过长,所以针对敏感的系统,无限排队的线程池是不合适的;另一种是基于数组实现的有界队列,队列的大小有限,所以排队的请求超过队列的大小时,接下来的请求就会被拒绝。

二、什么是队列 ?

队列和栈一样,也是一种 ‘操作受限’ 的线性表,只支持入队 enqueue 和出队 dequeue 两个操作,入队是将数据放到队列尾部,出队从对头取出数据。简单理解:队尾进 - 队头出。

队列作为一种非常基础的数据结构,应用也非常广泛,在很多偏低层系统、框架、中间件的开发中,起到关键性作用。比如高性能队列 Disruptor、Linux 环形缓存,都用到了循环并发队列;Java concurrent 并发包中利用 ArrayBlockingQueue 来实现公平锁等。

三、顺序队列和链式队列

用数组实现的叫顺序队列啊,用链表实现的叫链式队列。

以下代码展示如何用数组实现一个顺序队列:

public class ArrayQueue {

    private String[] iterms;
    private int head; //队头下标
    private int tail; //队尾下标
    private int n; //数组大小

    public ArrayQueue(int capacity) {
        this.iterms = new String[capacity];
        this.head = 0;
        this.tail = 0;
        this.n = capacity;
    }

    //队尾入队
    public boolean enqueue(String iterm) {
        if (tail == n) {//队列已满,不可以入队
            return false;
        }
        iterms[tail] = iterm;
        tail ++;
        return true;
    }

    //队头出队
    public String dequeue() {
        if (head == tail) {//表示队列为空
            return null;
        }
        String temp = iterms[head];
        head ++;
        return temp;
    }
}

比起用数组实现一个栈,队列的数组实现稍微有点复杂,对于栈来说,我们只需要一个栈顶指针就可以了,但是队列需要两个指针,一个是head,指向队头,一个是tail指针,指向队尾。

随着不停地入队、出队操作,head和tail都会持续往后移动,当tail移动到最右边:如果head还在队头,那么表示队列已满了,入队失败;如果head不在队头,那么为了充分利用head左边的空间,这时候会触发一次数据搬移,将head到tail之间的数据,整体搬移到数组中0到tail-head的位置。

基于链表的队列实现方法

基于链表实现同样也需要两个指针,大致实现思路是:链表尾部队尾入队时,tail.next = newNode,tail = tail.next ;从链表头部队头出队时,head= head.next。

四、循环队列

在用数组实现队列的时候,tail == n时会进行数据搬移,这样入队性能就会受到影响,这时可以尝试用循环队列解决。

队满的判断条件是(tail+1)% n = head

//队尾入队
public boolean enqueue(String iterm) {
    if ((tail + 1) % n == head) {//队列已满,不可以入队
        return false;
    }
    iterms[tail] = iterm;
    tail = (tail + 1) % n;
    return true;
}

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

 

五、阻塞队列和并发队列

阻塞队列其实就是在队列基础上增加了阻塞操作,简单来说,在队列为空时,从头取数据会被阻塞,知道队列中有数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。

线程安全的队列我们叫并发队列,最简单直接的方式就是直接在 enqueue() 和 dequeue() 方法上加锁,但是锁力度大并发度会比较低,同一时刻仅允许一个存或者取操作。这个时候可以实现一个无锁并发队列,网上有很多讨论,先不在此处详细解释。

总之,阻塞队列就是入队、出队操作可以阻塞,并发队列就是队列的操作多线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值