队列在线程池或其他有限资源池中的应用Application

注意:

CPU资源是有限的,任务的处理速度(velocity of processing)与线程个数(numbers of Thread)并不是线性正相关。On the contrary, 过多Too many的线程反而会导致lead to CPU频繁切换,处理性能下降。

Thereofore, 线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,从而来事先设置。

当我们向一个固定大小的线程池中请求一个线程时,but如果现在线程池中没有spare空闲资源了,那么now线程池会如何处理process这个request请求?拒绝还是排队请求处理策略又是什么?


上面所提出的问题和解决方案,就是我们今天要讲的主题——队列。

先进者先出就是典型的队列。
队列和栈其实是类似的,类似在哪里呢?——只支持两个基本操作——入队和出队

我们来看一张对比图——


队列和栈对比图

Queue的应用:比如高性能队列 Disruptor、Linux 环形缓存,都用到了循环并发队列;Java concurrent 并发包利用 ArrayBlockingQueue 来实现公平锁等。


顺序队列和链式队列

基于数组实现的队列: 对于栈来说,we just need 一个 栈顶指针就够了。
队列则需要两个,分别一个是head指针,指向队头;另一个是tail指针,指向队尾。


我们来通过一幅图来理解——


当 a、b、c、d 依次入队之后,队列中的 head 指针指向下标为 0 的位置,tail 指针指向下标为 4 的位置

然后呢,我们调用两次出队操作——

队列中 head 指针指向下标为 2 的位置,tail 指针仍然指向下标为 4 的位置

是的,随着不停地进行入队、出队操作,head 和 tail 都会持续往后移动(tail也会往后哦~~)。当 tail 移动到最右边,即使数组中还有空闲空间,也无法继续往队列中添加数据了。

那么这个问题该如何解决呢?
我们之前碰到过,数组的删除操作会lead to 数组中的数据data不连续。当时用了什么method呢?——数据搬移

每次出队操作都相当于删除数组下标为0的数据,if我们要搬移整个队列中的数据,这样出队操作的时间复杂度就会从O(1)变为O(n),我们怎么做可以优化这个呢?

实际上, 我们可以在出队时不用搬移数据,。

如果没有空闲空间了,我们just需要在入队时,再集中触发一次数据的搬移操作。

也就是说,我们不改变出队函数,改一下入队函数就可以。

我们来给一张示意图——


我们可以看到,当队列的 tail 指针移动到数组的最右边后,如果这个时候有新的数据入队,我们可以将 head 到 tail 之间的数据,整体搬移到数组中 0 到 (tail-head) 的位置。

给一个思考,这样的method, 出队操作的时间复杂度仍然是O(1),那么入队操作的时间复杂度呢?分析一下。


Then, 我们来看下基于链表的队列实现方法。

同样的,我们需要2个指针。分别指向链表的第一个结点和最后一个结点。我们来看示意图——

入队时,tail->next= new_node, tail = tail->next;出队时,head = head->next

循环队列

循环队列,它的产生是为了避免之前的一个问题。

上面我们用数组来实现队列的时候,在tail==n的时候,会产生数据搬移操作,In this way, 入队函数就要修改,我们来看看循环队列的解决思路——


循环队列和数组的区别:

数组是有头有尾的,一条直线。

然而循环队列则是首尾相连,扳成了一个环。我们来看张图——

循环队列示意图

我们可以看到,这个队列的大小为8,当前head为4,tail为7。when有一个新的元素a入队的时候,我们把a放入下标为7的位置。但这个时候,我们并不需要把tail更新为8,**而是将tail在整个环中后移一位,到下标为0的位置。 当再有一个新的元素b入队的时候,新的元素b放入下标为0的位置 ,然后tail加1更新为1 **,因此,当新的元素a b 依次入队以后,循环队列的元素编程了下面这样——

插入元素a b后

通过上述的循环队列的操作,我们避免了数据搬移的操作,但是,要写好循环队列的实现代码,最关键的是——确定好队列为空和队列为满的判定条件

我们来看看判定条件都有哪些注意的——基于数组实现的、非循环队列中,队满的判断条件是tail==n,**队空的判断条件是head==tail。 **

那么针对于循环队列呢?——

队列为空的判断条件仍然是head == tail, 但是队列为满的条件就比较复杂了,我们来看一张队列为满的图——

循环队列队列为满的示意图

上图中,实时情况是——tail = 3, head = 4, n = 8。我们总结一下规律就是**(3+1)%8=4 **。

当我们多花了几张队满的图时,就会发现规律是——**(tail+1)%n=head **。

而且,**当队列为满时,图中的tail指向的位置实际上没有存储数据 ,也就是说,循环队列会浪费一个数组的存储空间 **。


阻发队列和并发队列

这两个队列属于比较特殊的队列。

阻发队列 , 就是在队列的基础上增加了阻塞操作。In other words, 就是在队列为空的时候,从队头取数据会被阻塞 , why? 因为此时还没有数据可以取,需要等到队列中有了数据后才能返回。

如果**队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲spare位置后再插入数据,然后再返回。 **

生产者-消费者模型

在上图的“生产者-消费者模型”中,当生产者生产的速度过快,消费者来不及消费时,存储数据的队列很快就会变满。

In this time, 生产者就会阻塞等待, 直到消费者消费了数据,生产者才会被wake up从而继续生产

of course, 我们还可以多配置2个消费者,来应对一个生产者。见图——

多个消费者

在多线程的情况下,会有多个线程同时操作队列,这个时候线程的安全性就需要我们来考虑,那么如何实现一个线程安全的队列呢?

我们来看看并发队列, 最简单的实现方式则是直接在入队和出队的函数上加锁,但是锁粒度大并发度就会比较低。同一时刻仅仅允许一个或者操作。

实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。

我们现在来考虑一个问题——

当线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?

第一种:非阻塞的处理方式,直接拒绝任务请求。

第二种:阻塞的处理方式,将请求排队,当有空闲线程时,取出排队的请求继续处理。


我们希望可以公平的处理每个排队的请求,先进者先服务。

  • 基于链表的实现, 可以实现一个支持无限排队的无界队列,但may导致过多的请求排队等待,响应时间过长。
  • 基于数组的实现, 数组实现的通常是有界队列,队列的大小有限。当请求过多时,following的请求就会被拒绝。

队列可以实现的场景有哪些?

比如数据库连接池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。

阻塞队列就是入队和出队时,操作可以进行阻塞。

并发队列就是队列的操作多线程安全。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容