选对线程安全容器关键看读写比例、是否需要阻塞、一致性要求和内存性能敏感度;读多写少用copyonwrite系列,读写均衡高频选concurrenthashmap,需阻塞用blockingqueue,单变量更新用原子类。

选对线程安全容器,关键不是看“能不能并发”,而是看“怎么并发”——读写比例、是否需要阻塞、一致性要求、内存与性能敏感度,这四点决定了哪个容器真正适合你。
先看读写特征:读多写少就用 CopyOnWrite 系列
如果集合主要用于遍历(比如监听器列表、配置白名单、规则缓存),而增删极少(每秒不超过几十次),CopyOnWriteArrayList 或 CopyOnWriteArraySet 是优选。
- 读操作完全无锁,get() 和迭代都极快
- 每次写操作(add/remove/set)复制整个数组,所以写开销大、GC 压力高
- 迭代器基于创建时刻的快照,不会抛 ConcurrentModificationException,也不会看到新写入的元素
- 不适合实时性要求高的场景,比如“刚加进去就要被另一个线程立刻查到”
读写均衡且高频,首选 ConcurrentHashMap
这是大多数业务场景的默认选择,尤其当多个线程频繁读取和更新键值对时(如用户会话缓存、计数统计、路由映射)。
- JDK 8+ 使用 CAS + synchronized 锁单个桶(链表头或红黑树根),不是全表锁
- 读操作无锁,写冲突只影响局部,吞吐量远高于 synchronizedMap
- 键和值都不能为 null(避免歧义,这点和 HashMap 不同)
- 别用 containsKey() + put() 这类组合操作——它不是原子的;改用 putIfAbsent()、computeIfAbsent() 或 merge()
- size() 返回的是估算值,判断空请用 isEmpty()
需要阻塞等待或任务交接,上 BlockingQueue
典型用于生产者-消费者模型,比如异步日志、线程池任务队列、消息中转。
- ArrayBlockingQueue:有界、基于数组、可重入锁,适合容量可控、强调稳定性的场景
- LinkedBlockingQueue:默认无界(注意 OOM 风险)、读写分离双锁,吞吐通常更高
- PriorityBlockingQueue:带优先级的无界队列,元素需实现 Comparable
- SynchronousQueue:不存数据,每个 offer 必须配一个 poll,适合直接移交任务
只是单个变量更新,别绕弯子——用原子类
如果目标不是维护整个集合结构,而是计数器、开关标志、或替换整个引用,AtomicInteger、AtomicReference 等更轻量高效。
- AtomicInteger.incrementAndGet() 是无锁原子操作,比 new ConcurrentHashMap
存单个计数器合理得多 - AtomicReference
- > 可安全替换整个列表引用,但注意内部 List 本身仍需线程安全(比如用 CopyOnWriteArrayList)
- 涉及多个字段协同更新(如余额+状态),原子类不够用,考虑 StampedLock 或显式 synchronized 块
不复杂但容易忽略:没有“万能线程安全集合”,只有“更适合当前访问模式”的集合。先理清你的读写频率、是否允许弱一致性、要不要阻塞、有没有内存限制,再对照特性匹配,比盲目套用 synchronized 包装或一律上 ConcurrentHashMap 更可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











