theme: smartblue
队列是常用的数据结构之一,是一种先入先出(First Input First Output)的结构。
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
使用场景
队列一般用于生产者-消费者模式。队列有阻塞队列和非阻塞队列,两者区别为当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。这两者都能用于生产者-消费者模式,但是在选择上有细微区别。
- 阻塞队列
- 供小于求的情况使用阻塞队列更加合适。如果不使用阻塞队列,则会不断轮询,耗费资源。为防止消费进程长时间等待,可以设置超时时间来解决这个问题。
- 阻塞队列为有界队列,能够设置队列长度
- 非阻塞队列
- 非阻塞队列性能更高
- 对于实时性不那么高的场景,生产者不断生产,消费者定时消费
- 非阻塞队列为无界队列
项目中自己写阻塞队列的情况比较少,一般消费MQ的时候会自动阻塞,超过一定时间没有消息会自动断开链接。
非阻塞队列在商品收藏功能上用过。因为商品收藏功能QPS相对高,而且会直接操作DB,导致服务不稳定。收藏业务不需要特别实时,点击收藏按钮后按钮变红,用户查看收藏列表能查看到收藏的商品,很少有人点击收藏后立即查看收藏列表。所以使用Redis的队列存储收藏请求,起定时脚本每分钟定时进行消费。
线程安全的并发队列
队列使用起来方便,但在实际生产中,需要考虑并发情况下,线程安全问题。
对于Java来说,本身提供了线程安全的并发队列:
- 非阻塞队列:ConcurrentLinkedQueue(无界线程安全),采用CAS机制(compareAndSwapObject原子操作)。对于CAS机制,建议看线程安全的并发队列,https://blog.csdn.net/liuxiao723846/article/details/88382520 讲解的十分详细,而且能够感受到算法的精妙。
- 阻塞队列:ArrayBlockingQueue(有界)、LinkedBlockingQueue(无界)、DelayQueue、PriorityBlockingQueue,采用锁机制;使用 ReentrantLock 锁。对于ReentrantLock,建议看ReentrantLock 锁详解https://blog.csdn.net/zhengzhaoyang122/article/details/110847701,里面包含大量细节。
对于Go来说,有双向链表list,可以实现队列的功能,但该结构并不是线程安全的,如果想并发使用,需要使用sync保证线程安全。
总结
重新看一下基础知识挺有意思的,总能发现很多新的知识点,也能将以前用过的知识归纳总结,还能提供一些新的写作思路。写这篇博客的时候,发现锁可是太有意思了,所以下一篇博客我打算写一下Go语言里的锁,欢迎大家到时候观看。
资料
- 阻塞队列与非阻塞队列区别应用场景
- 阻塞队列的使用场景?
- 简单,高效,实用的非阻塞(无锁)和阻塞并行队列算法
- 阻塞队列与非阻塞队列的区别
- [怀旧并发06]分析ReentrantLock的实现原理
- 什么是可重入
- 线程安全的并发队列
- CAS无锁算法与ConcurrentLinkedQueue
- ReentrantLock 锁详解
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
往期文章回顾: