- 1. 阻塞队列(线程安全)
- 1.1. 非阻塞队列
- 1.2. 阻塞队列脑图
- 1.3. 阻塞的意思
- 1.4. 主要方法
- 1.5. 1.array数组结构有界全局锁(初始化时必须指定大小)
- 1.6. 2.linked单链表结构无界两锁(可指定大小)
- 1.7. 3.priority优先级排序数组扩容无界
- 1.8. 4.delay优先级队列实现的无界
- 1.9. 5.synchronous不存储元素(newCachedThreadPool默认队列)
- 1.10. 6.LinkedTransferQueue链表无界,预占
- 1.11. 7.LinkedBlockingDeque双向链表,队头队尾均可加减,工作窃取(锁竞争最多降到一半)
- 1.12. array和linked的比较
阻塞队列(线程安全)
非阻塞队列
- 线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue。
- 线程安全就是说多线程访问同一代码,不会产生不确定的结果。

concurrent*的lock-free
有时候我们把并发包下的所有容器都习惯的称为并发容器,但是严格来讲,只有Concurrent*才是真的并发容器。
关于问题中的区别在于:
Concurrent类型基于lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量。
lock-free:如果在一个共享的数据结构上的操作都不需要互斥,那么它是无锁的。如果一个进程在操作中间被中断,其它进程不受影响。
举例:如果一个线程拥有互斥锁,当运行线程的时候处于休眠,就会block,但lock-free模式下不会阻塞。
LinkedBlockingQueue内部是基于锁,提供BlockingQueue等特性方法
同样是线程安全的容器,可简单认为:
Concurrent类型没有类似CopyOnWrite之类的容器相对较重的修改开销
Concurrent拥有较低的遍历一致性,也就是弱一致性。当使用遍历方法中的迭代器遍历时,容器发生修改,迭代器仍然可以继续遍历
注:与弱一致性相对的,如果遍历过程出了问题,就会抛出异常ConcurrentModifcationException,停止遍历。
concurrent的弱一致性,导致对size的操作精准度有限。
concurrent读取性能不佳。
————————————————
版权声明:本文为CSDN博主「2NaCl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41936805/article/details/96307821
阻塞队列脑图

阻塞的意思
1.队列没数据,消费端阻塞
2.队列满,生产端阻塞
主要方法

特殊值指回复null or false。
详细的解释:https://blog.csdn.net/org_hjh/article/details/110195517

1.array数组结构有界全局锁(初始化时必须指定大小)
arrayBlockingQueue
默认不公平锁,创建一个公平锁的阻塞队列:
1 | ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true); |
2.linked单链表结构无界两锁(可指定大小)
linkedBlockingQueue
- 对于生产者端和消费者端分别采用了独立的锁来控制数据同步。LinkedBlockingQueue是由链表组成操作的分别是头尾节点,相互竞争的关系较小。而ArrayBlockingQueue是数组,添加和删除都是在同一个数组上,虽然也可以用两个锁但是实现上需要更多的控制。
- LinkedBlockingQueue 会默认一个类似无限大小的容量(Integer.MAX_VALUE)。
3.priority优先级排序数组扩容无界
PriorityBlockingQueue 优先级队列,线程安全(添加、读取都进行了加锁)、无界、读阻塞的队列,底层采用的堆结构实现(二叉树),默认是小根堆,最小的或者最大的元素会一直置顶,每次获取都取最顶端的数据。可以实现优先出队。最特别的是它只有一个锁,入队操作永远成功,而出队只有在空队列的时候才会进行线程阻塞。可以说有一定的应用场景吧,比如:有任务要执行,可以对任务加一个优先级的权重,这样队列会识别出来,对该任务优先进行出队。
元素采取自然顺序升序排列。可以自定义实现compareTo()方法来指定元素进行排序规则,或者初始化 PriorityBlockingQueue 时,指定构造参数 Comparator 来对元素进行排序。需要注意的是不能保证同优先级元素的顺序。
4.delay优先级队列实现的无界
内部使用优先队列(PriorityQueue)实现,而无界队列基于数组的扩容实现。在创建元素时,可以指定多久才能从队列中获取当前元素。只有延时期满后才能从队列中获取元素。
应用场景:
- 缓存设计,有效期控制
- 定时任务调度,保存当天执行任务和执行时间
5.synchronous不存储元素(newCachedThreadPool默认队列)
SynchronousQueue 的吞吐量高于LinkedBlockingQueue 和ArrayBlockingQueue。
相对比较另类的SynchronousQueue,在Java 6中,其实现发生了非常大的变化,利用CAS替换掉了原本基于锁的逻辑,同步开销比较小。它 是Executors.newCachedThreadPool()的默认队列。
原因:
- 不像ArrayBlockingQueue、LinkedBlockingDeque之类的阻塞队列依赖AQS实现并发操作,SynchronousQueue直接使用CAS实现线程的安全访问。(自旋可以显著改变吞吐量。)
6.LinkedTransferQueue链表无界,预占
有就拿货走人,没有就占个位置,等到或超时。
7.LinkedBlockingDeque双向链表,队头队尾均可加减,工作窃取(锁竞争最多降到一半)
Deque 是 Double ended queue (双端队列) 的缩写,读音和 deck 一样,蛋壳。
双端队列同样适用于另一种相关模式,即工作密取。在生产者-消费者设计中,所有消费者有一个共享的工作队列,而在工作密取设计中,每个消费者都有各自的双端队列。如果一个消费者完成了自己双端队列中的全部工作,那么它可以从其它消费者双端队列末尾秘密地获取工作。密取工作模式比传统的生产者-消费者模式具有更高的可伸缩性,这是因为工作者线程不会在单个共享的任务队列上发生竞争。在大多数时候,它们都只是访问自己的双端队列,从而极大地减少了竞争。当工作者线程需要访问另一个队列时,它会从队列的尾部而不是头部获取工作,因此进一步降低了队列上的竞争程度。
适用场景
工作密取非常适用于即是消费者也是生产者问题。
当执行某个工作时可能导致出现更多的工作。
例如,在网页爬虫程序中处理一个页面时,通常会发现有更多的页面需要处理。类似的还有许多搜索图的算法,例如在垃圾回收阶段对堆进行标记,都可以通过工作密取机制来实现高效并行。当一个工作线程找到新的任务单元时,它会将其放到自己队列的末尾(或者在工作共享模式中,放入其它工作者线程的队列中)。当双端队列为空时,它会在另一个线程的队列末尾查找新的任务,从而确保每个线程都保持忙碌状态。
————————————————
版权声明:本文为CSDN博主「老马啸西风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ryo1060732496/article/details/88889886
array和linked的比较
上面举出的LinkedBlockingQueue、ArrayBlockingQueue为例子,需求可以从很多方面去考虑:
- 考虑应用场景中对队列边界的要求。ArrayBlockingQueue是有明确的容量限制的,而LinkedBlockingQueue则取决于我们是否在创建时指定,SynchronousQueue则干脆不 能缓存任何元素。
- 从空间利用角度,数组结构的ArrayBlockingQueue要比LinkedBlockingQueue紧凑,因为其不需要创建所谓节点,但是其初始分配阶段就需要一段连续的空间,所以初始内存 需求更大。
- 通用场景中,LinkedBlockingQueue的吞吐量一般优于ArrayBlockingQueue,因为它实现了更加细粒度的锁操作。
ArrayBlockingQueue实现比较简单,性能更好预测,属于表现稳定的“选手”。
————————————————
版权声明:本文为CSDN博主「2NaCl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41936805/article/details/96307821