跳转到内容

并发容器

用途:多线程共享集合时选择正确容器,避免“偶发数据错乱”

场景 选择 说明
高并发读写 Map ConcurrentHashMap 分段/CAS + 细粒度锁,读无锁
需要顺序的并发队列 ConcurrentLinkedQueue(无锁)/ LinkedBlockingQueue(锁 + 有界) 有界队列可做背压
阻塞取任务 ArrayBlockingQueue / LinkedBlockingQueue / SynchronousQueue 线程池工作队列基础
优先级 + 超时 PriorityBlockingQueue 无界,注意内存
延迟执行 DelayQueue 定时任务、超时关闭
读多写少、快照读 CopyOnWriteArrayList 写时复制,写代价高,禁止大集合频繁写
高频计数 LongAdder 优于 ConcurrentHashMap 计数
并发流处理 parallelStream + 共享 ForkJoinPool IO 任务不适合(池共享)
  1. 单个操作原子(put、putIfAbsent、compute、merge、computeIfAbsent),但**“先 get 再 put”的组合不原子**——必须用 computeIfAbsent/merge 表达。
  2. 迭代器弱一致:不抛 ConcurrentModificationException,但可能看到也可能看不到并发修改,不适合做“精确快照统计”。
  3. 键值不得为 null:无法区分“不存在”与“值为 null”。
  4. size() 是估算:并发写时不是精确值;需要精确计数用累加器或外部计数。
  5. computeIfAbsent 内不要做远程调用或递归修改同一 map:可能长时间持锁或死锁。
  • 用 offer(timeout) / poll(timeout) 而不是无限 put,避免上游被永久阻塞。
  • 队列长度是背压信号:持续接近容量说明消费能力不足,应扩容消费者或拒绝部分请求,而不是加大队列(只是把延迟藏起来)。
  • 关闭时先停止生产,再消费完剩余(poison pill 或 shutdown + awaitTermination)。

Collections.synchronizedMap、Hashtable、Vector 用整体互斥,扩展性差,新代码应使用并发容器;synchronizedList 迭代仍需手动加锁。

误区 修正
用 ConcurrentHashMap 仍写 if(!map.containsKey(k)) map.put(...) 用 computeIfAbsent / merge
用 CopyOnWriteArrayList 承载高频写 改读写锁或分片结构
无界队列当“不会丢数据” 内存耗尽或延迟不可控,必须设上限与拒绝
依赖 size() 做精确限流 用独立计数器或令牌桶
在 computeIfAbsent 里调用 RPC 锁持有时间不可控,改为外部预取或异步加载
  • 《Java 并发编程实战》第 5 章(并发容器与同步队列)
  • Doug Lea《JSR-166 Interest Guide》
  • OpenJDK java.util.concurrent 包文档