提到队列,咱们会在不少地方听到或者看到,数组
那咱们来看一下这位不太说话的老朋友,缓存
从栈很容易联想到队列的实现安全
一个数组实现的顺序队列,在 入队了 AA 、BB 、CC 后,数据结构
队头指针 head=0,队尾指针 tail=3。以下图:多线程
紧接着,又有两次出队,一样,对于出队head指针日后移动两个:并发
以上两个图对应的如队出队操做,也是很容易看出问题所在:优化
随着入队出队一波操做,tail指针很容易移动到最后的位置,表面上不能再入队了。spa
可是极有可能如图二同样,头指针head前面有大片空地。操作系统
怎么办?搬!我在出队以后,后面的数据往前挪,咱们能够称之为移动补位。线程
可是每一次出队操做都去搬数据,时间复杂度想一想就会很高 O(n)
怎么优化?
tail指针抵达末尾,同时head指针不在队头。也就是tail到了最后,且head前面有空。
此时触发数据搬移,过程以下:
人的思想不断进步,而且思考如何作得更加轻巧灵活。
咱们会思考,可不能够不用搬移数据呢?
能够,接下来轮到循环队列登场了。。。。。。
循环队列,顾名思义。首尾相连造成环。哝,就是这个样子:
长得这么好看,必定要对得起咱们对它的指望。
通过一番出队入队,头部索引=2,尾部指针指向最后一个位置,即将接受FF入队,
此时看上去又到了挪动数组的时候了?
环形的存在就是为了不队列的数据搬移,我想你已经想到了它的灵巧之处。
对,就是将数据FF填充到索引=5处,tail指针移动到下一个,也就是索引=0处,就成了这样:
队列在平时工做时用的机会场景比较少,可是在一些偏底层系统中确实应用比较普遍。
好比:阻塞队列、并发队列
阻塞队列,就是在队空时,取数据会被直接拒绝。直到有数据才会容许被访问。
这种模型相似于 生产-消费关系,对的,这也是不少的消息队列的思想和应用。
这种阻塞队列能够协调生产和消费的关系。固然,也能够生产的i消息被多个消费。
这又产生了一个线程并发问题,咱们如何保证线程安全呢?这就须要并发队列。
基于数组的循环队列+CAS原子操做,能够很好的实现无锁并发队列。
基于以上,微软给咱们所提供的这些源码:
咱们着重看一下泛型队列和并发泛型队列
队列 Queue 、泛型队列 Queue<T>
咱们直接看一下泛型版本的:
0、注释说明:这是一个基于数组实现的环形队列,也就是循环队列
一、初始定义
二、重要的私有变量
三、入队:分为两块主逻辑,一个是队满,一个是正常插入。
第0步已经注释说明这是一个循环队列,因此咱们借此机会分析一下这个循环队列。
if (_size == _array.Length) 2倍扩容而且有最小装载量判断。
_tail = (_tail + 1) % _array.Length; 下面咱们来看看这句话怎么来的。
对于非循环队列,头尾指针和数组的关系好确认。
而循环队列,由于是一个环,因此怎样定位移动后的指针位置才是关键的。
数组长度=6
当我入队FF,原来尾部指针=5,当前尾部指针=0;
接着入队GG, 原来尾部指针=0,当前尾部指针=1;
当我入队HH,原来尾部指针=1,当前尾部指针=2;
规律:当前指针 = (原来指针 +1) % 数组长度
四、出队同3
ConcurrentQueue<T>
注释说的很明白,这是一个无锁并发队列
咱们在看源码以前先来了解一些定义
对于如今的多CPU、以及超线程概念的操做系统来讲,CPU和内存以前存在处理速度上的差距,因此中间加了寄存器和高速缓存来缓冲。
多线程并发状况下,多核计算机,一个CPU读取的是在寄存器中的值,另外一个CPU读取的是内存中的值,这就形成了数据不一样步。
对于产生的并发问题,咱们来看看并发队列对这些的处理。
咱们先来理解接下代码中涉及到的名词:
一、易失结构 volatile : 告诉编译器和CLR不须要优化代码顺序,使得代码可控。不用将字段缓存到寄存器,缓存早内存中就行。
二、互锁结构 Interlocked : CAS保证原子性读取操做
三、自旋锁 :原地打转,直到达到条件才离开。对于线程来说,一直持有资源不撒手。
四、线程类提供了几个方法:
五、CAS理论:compare and swap 比较并交换。该操做经过将内存中的值与指定数据进行比较,当数值同样时将内存中的数据替换为新的值。
天也不早了,人也很多了,让咱们干点正事。简单看看入队和出队操做。
入队:
需求是怎样保证入队的原子性?
经过 Interlocked 声明同步块,只容许一个线程抢占资源进行入队,其余线程使用自旋锁进行原地等待。
等当前线程释放同步块,其余线程再次抢占同步块,而后入队。直到队满跳出。
出队:也是相似,经过自旋锁,抢占同步块进行原子性出队操做。
最后咱们再来悄悄看看 自旋锁自旋逻辑:
自旋至少10次,而后进行相应的自旋等待,而且相应的让出本身的时间片,让其余低级别线程能够获得调度。
整体来讲,并发队列经过CAS进行原子性入队和出队,并结合自旋锁进行抢占资源。
也就是不少的线程并发入队或者出队,同一时刻只有一个能够进行原子性入队出队。