ConcurrentLinkedQueue 的无锁队列,适合什么场景

ConcurrentLinkedQueue 的无锁队列,适合什么场景

ConcurrentLinkedQueue 的无锁队列,适合什么场景


「ConcurrentLinkedQueue 的无锁队列,适合什么场景」


去年在优化一个相机预览模块的帧缓冲区时,我把原来的 LinkedBlockingQueue 换成了 ConcurrentLinkedQueue。当时的想法很直接:无锁队列,没有锁竞争,多线程环境下性能应该更好。结果上线后,用户反馈的是手机发烫,而不是变快了。抓了一段 systrace 一看,CPU 时间没有明显减少,GC 的频率反而翻了一倍。问题就出在这个"无锁"上。


帧缓冲池里的 GC 抖动


那个模块负责管理相机预览的 SurfaceImage 对象池。业务逻辑是后台线程从相机拿帧,处理后抛给主线程渲染。为了复用对象,我们维护了一个 ConcurrentLinkedQueue<ImageWrapper> 作为缓冲池。代码大概长这样:


ConcurrentLinkedQueue<ImageWrapper> pool = new ConcurrentLinkedQueue<>();

// 释放回池
pool.offer(wrapper);

// 取用
ImageWrapper item = pool.poll();

单看 API 设计,offer 和 poll 都是无锁的,多线程并发应该很高效。但在 Android Studio Profiler 的 Memory 视图里,这个池子每秒钟产生数百个 Allocation,而且绝大部分不是 ImageWrapper 本身,而是 ConcurrentLinkedQueue 的内部节点 Node。在 Android 13(API 33)的 ART 环境下,每个被回收的 Node 对象都要走完整的 GC 路径。当相机帧率达到 30fps,池子流转频繁时,这些临时 Node 对象成了 GC 的主要压力来源。


ConcurrentLinkedQueue 的链表结构决定了每次 offer 都要 new 一个 Node,poll

Shizuku 权限方案,无 Root 调用系统 API 的实践 2026-08-06
Coil 和 Glide 的加载性能对比数据 2026-08-06

评论区