并发容器:CHM、CopyOnWrite、阻塞队列的选型与常见坑
·
并发容器的本质不是“线程安全”四个字,而是:
- 在不同读写比例下,怎么用不同的数据结构把性能与一致性做到可控
这篇按“选型 -> 原理 -> 坑 -> 排查”的结构讲:
- ConcurrentHashMap(CHM)
- CopyOnWriteArrayList
- BlockingQueue(阻塞队列)
1. ConcurrentHashMap:你至少要讲清 3 个点
1.1 为什么 HashMap 线程不安全
- 扩容/链表转换过程中可能出现数据丢失、死循环等问题(历史版本更明显)
1.2 CHM 怎么做到高并发
你可以按版本讲:
- JDK7:分段锁(Segment)
- JDK8:CAS + synchronized(桶级锁)+ 红黑树
工程上你只需要把结论讲清:
- 锁粒度从“全表”降到“桶/节点”,并在大部分情况下走 CAS 快路径
1.3 put/get 的性能特点
- get 大多无锁
- put 在冲突桶上加锁(synchronized)
2. CHM 的常见坑
- 把 CHM 当成“强一致 map”:并发下 size/遍历是弱一致
- computeIfAbsent 里做慢操作:可能导致桶锁长时间占用
- 把大对象塞进去:内存压力与 GC 压力上升
排查建议:
- 如果发现某个 key 热点更新,考虑分段 key 或引入 LongAdder 聚合
3. CopyOnWriteArrayList:读多写少的利器
核心机制:
- 写操作时复制一份新数组,修改后再替换引用
- 读操作不加锁,直接读当前数组引用
适用场景:
- 读多写少(例如白名单、配置快照、监听器列表)
不适用:
- 写频繁(复制数组成本很高)
4. BlockingQueue:线程池与生产消费的核心组件
常见实现:
- ArrayBlockingQueue:有界、数组、一个锁(公平可选)
- LinkedBlockingQueue:可有界也可无界(无界风险大)
- SynchronousQueue:不存储,直接交接
- DelayQueue:延迟任务
工程选型:
- 需要背压:优先有界队列
- 低延迟短任务:可考虑 SynchronousQueue(配合合理 max)
5. 阻塞队列常见坑
- 无界队列堆积:最终 OOM 或延迟爆炸
- 把阻塞队列当缓存:阻塞队列不是缓存(更像协调组件)
- 生产快于消费:需要背压、限流、或扩容消费者
6. 线上排查:容器导致的性能问题怎么看
- CPU 高:可能是热点 key 自旋/CAS 重试,或 computeIfAbsent 慢逻辑
- RT 抖:可能是桶锁竞争、写放大、或队列堆积
- OOM:可能是无界队列、或 map/列表塞了大对象
7. 面试追问 Q&A
- Q:CHM 为什么 get 快?
- A:读大多无锁,依赖 volatile/内存语义保证可见性。
- Q:CopyOnWrite 为什么适合读多写少?
- A:读无锁,写复制数组,写成本高。
- Q:阻塞队列和普通队列有什么本质区别?
- A:阻塞队列提供阻塞/唤醒语义,用于线程协作与背压。
8. 面试表达(30 秒讲清楚)
- CHM 在 JDK8 主要靠 CAS + 桶级锁提高并发度,get 大多无锁,put 在冲突桶上加锁。
- CopyOnWrite 写时复制,读无锁,适合读多写少。
- BlockingQueue 是线程协作与背压组件,选型关键是是否有界、是否允许堆积。
9. 总结
- 并发容器选型看读写比例与背压需求
- CHM 不是强一致结构,热点 key 仍可能成为瓶颈
- CopyOnWrite 写成本高,别用错场景
- 阻塞队列有界优先,避免无界堆积
更多推荐

所有评论(0)