Java线程安全集合选型需按场景:读多写少用CopyOnWriteArrayList,高并发读写用ConcurrentHashMap,强一致性用BlockingQueue,简单共享用AtomicInteger等原子类。

Java中选择线程安全的集合,关键不是“有没有锁”,而是看场景:读多写少、写频繁、需要强一致性、还是允许弱一致性?不同集合的设计目标差异很大,选错不仅性能差,还可能出并发bug。
读多写少场景:优先用 CopyOnWriteArrayList 和 CopyOnWriteArraySet
适合迭代远多于增删的场景,比如监听器列表、配置白名单。它的原理是每次写操作都复制整个数组,读完全无锁、高性能,但写操作开销大、内存占用高。
- 迭代时不会抛
ConcurrentModificationException,也不需要加锁 - 不适用于高频写(如每秒数百次 add/remove),否则 GC 压力明显
- 不支持在迭代过程中修改(虽然不报错,但修改的是旧副本,新副本不可见)
高并发读写均衡:首选 ConcurrentHashMap
这是最常用的线程安全 Map,JDK 8 后基于 CAS + synchronized 分段锁优化,读操作几乎无锁,写操作只锁对应 bin 链表或红黑树头节点。
- 不支持
containsKey+put这类复合操作的原子性,需用computeIfAbsent或merge - 遍历时用
entrySet().forEach(...)是弱一致性快照,可能看不到最新写入,也不会阻塞写入 - 避免用
size()判断是否为空——它返回估算值;要用isEmpty()(它是准确的)
需要强一致性和阻塞行为:考虑 BlockingQueue 系列
比如 ArrayBlockingQueue(有界、可重入锁)、LinkedBlockingQueue(默认无界、读写双锁)、PriorityBlockingQueue(带排序的无界队列)。它们专为生产者-消费者模型设计,支持阻塞插入/获取、超时等待、容量控制。
立即学习“Java免费学习笔记(深入)”;
- 有界队列(如 ArrayBlockingQueue)能防止内存溢出,适合资源受限系统
- 不要在循环里反复调用
poll()空转,应使用take()或带 timeout 的poll(long, TimeUnit) -
SynchronousQueue不存储元素,每个put必须配一个take,适合任务交接场景(如 newCachedThreadPool 内部使用)
简单计数或状态共享:别造轮子,直接用 AtomicInteger、AtomicReference 等原子类
如果只是做计数器、开关标志、单个对象引用更新,比用 ConcurrentHashMap 或包装成 synchronized 方法更轻量、更高效。
-
AtomicInteger的incrementAndGet()、compareAndSet()是无锁且原子的 -
AtomicReference可用于替换整个集合引用(注意内部 List 本身仍需线程安全) - 复杂逻辑若涉及多个变量协同更新,原子类不够用,应考虑
StampedLock或显式锁
不复杂但容易忽略:没有“万能线程安全集合”,只有“更适合当前访问模式”的集合。先理清读写比例、是否需要阻塞、一致性要求、内存敏感度,再对照特性选型,比盲目套用 synchronized 包装或一律上 ConcurrentHashMap 更可靠。










