Java-多线程并发之并发队列原理剖析
CAS实现非阻塞队列ConcurrentLinkedQueue:
维护一个无边界的单向链表。非阻塞的CAS,没有加锁而是多次循环去替代阻塞的消耗,会出现弱一致性的问题。
第一次执行Peek或first会把head指向队列真正的第一个元素。
节点head和tail是volatile的(可见性保证),只需要保证操作的原子性即可。
offer(在队尾添加元素)操作:在队尾通过CAS加元素,具体方法是寻找队尾的next。如果poll操作后可能会把head变为自引用,需要重新寻找head。
如果CAS不成功就循环,牺牲CPU资源换取阻塞带来的开销。
poll(在队列头部移除一个元素):从队头开始迭代,将头节点的元素的item设置为null来移除,然后设置新的头节点。被移除的节点会被垃圾回收。如果在操作过程中头节点被修改,需要重新循环。
peek(获取队列头部元素):和poll类似,只是没删除。第一次调用peek,会删除哨兵节点,并让队列的head节点指向队列里面第一个元素或者null.
size:统计队列元素个数。因为CAS没有加锁,所以一致性不好。
remove:如果队列存在元素则删除元素,存在多个删除第一个。
contains:也是会存在不一致的问题。
独占锁阻塞队列LinkedBlockingQueue:
基于链接,无界的FIFO阻塞队列。
头尾各维护一个条件变量,和AQS队列进行操作。
ArrayBlockingQueue:
有界(初始化设定)FIFO阻塞队列。
多线程环境下不保证“公平”
队尾插入:
在队列满的时候
offer直接false不阻塞
put阻塞等待。响应中断
队头删除:
队头为空
poll返回null
take阻塞等待。响应中断
PriorityBlockingQueue:
基于平衡二叉树堆(最大堆最小堆)带优先级的无界阻塞队列。 可以自定义Comparators。默认用CompareTo
扩容:小于64每次+2 否则就右移一位。
二叉堆的实现:
通过记录来存在一个数组里
记录父元素的位置parent和插入节点的位置k 插入的值key e是父节点元素的值,进行比较。
这里只用了NotEmpty,因为是无界队列,永远都不会用到NotFull,put操作也永远不会处在await状态,所以不需要唤醒。
最大堆:父节点的键值总是大于或等于任何一个子节点的键值
最小堆:父节点的键值总是小于或等于任何一个子节点的键值
添加操作offer则是不断的上冒的过程 SiftUpComparable
删除操作poll作则是不断你的下掉的过程 SiftDownComparable
DelayQueue:
无界阻塞延迟队列。
ReentrantLock+Condition+根据Delay时间来排序的优先队列:PriorityQueue
队列的每个元素都有一个过期时间,当从队列获取元素,只有过期的元素才会出队列,队列的头元素是快要过期的元素。
应用:
1清理超时的缓存。
2任务超时处理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!