队列

队列

先进先出,后来的只能插入到队尾

操作

入队:enqueue(),出队dequeue()

应用

循环队列(高性能队列DIsruptor,Linux环形缓存)、阻塞队列、并发队列,Java concurrent 并发包利用 ArrayBlockingQueue 来实现公平锁等

实现

数组实现顺序队列,链表实现链式队列

队列需要两个指针,一个是head指针指向第一个数据,一个是tail指针指向下一次要插入数据的位置

数据搬移

解决数组空间用完问题。优化搬移操作:如果每删除一个数据就搬移一次,出队的时间复杂度就会由O(1)变为O(n),优化之后将出队保持不变,在入队时判断集中触发搬移操作,入队的时间复杂度还是O(1)

public boolean enqueue(String item) {
    if (tail == n) {
        if (head == 0) return false;
        for (int i= head; i < tail ; i++ ) {
            items[i - head] = items[i];
        }
        tail = tail - head;
        head = 0;
    }
    
    items[tail] = item;
    tail++;
    return true;
}

循环队列

解决数据入队搬移操作影响性能问题。最主要就是判断队空和队满条件

//判断队空
head == tail
//判断队满
head == (tail + 1) % n
    

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

public String dequeue(String item) {
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
}

阻塞队列和并发队列

阻塞队列:在原来队列中添加阻塞操作,实现“生产者-消费者模型”。

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

补充

当线程池没有空闲资源,这时有新的任务请求线程资源,一般有两种处理方式,一种是非阻塞处理方式,直接拒绝请求任务,一种是阻塞式处理方法,将请求排队,当有空闲资源时在将请求取出处理,这是就用到队列了。

基于链表的实现方式,可以实现一个支持无限排队的无界队列(unbounded queue),但是可能会导致过多的请求排队等待,请求处理的响应时间过长。所以,针对响应时间比较敏感的系统,基于链表实现的无限排队的线程池是不合适的。

基于数组实现的有界队列(bounded queue),队列的大小有限,所以线程池中排队的请求超过队列大小时,接下来的请求就会被拒绝,这种方式对响应时间敏感的系统来说,就相对更加合理。不过,设置一个合理的队列大小,也是非常有讲究的。队列太大导致等待的请求太多,队列太小会导致无法充分利用系统资源、发挥最大性能。

除了前面讲到队列应用在线程池请求排队的场景之外,队列可以应用在任何有限资源池中,用于排队请求,比如数据库连接池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值