copyonwritearraylist写慢因每次修改复制整个数组(o(n)),读不加锁因直接访问当前数组引用,适合读多写少场景,但迭代时看不到新元素且不支持remove()。

CopyOnWriteArrayList 为什么写操作慢但读不加锁
因为每次 add、set、remove 都会复制整个底层数组,写操作时间复杂度是 O(n),而读操作直接访问当前数组引用,无同步开销。适合读远多于写的场景,比如监听器列表、配置快照。
常见错误现象:ConcurrentModificationException 不会抛出,但迭代期间看不到刚写入的元素——因为迭代器持的是旧数组快照。
- 迭代器不支持
remove()(会抛UnsupportedOperationException) - 不能用于需要实时强一致性的场景,比如库存扣减
- 内存占用翻倍:新旧数组同时存在,GC 压力大
ConcurrentHashMap 的分段锁和 CAS 是怎么配合的
ConcurrentHashMap 在 JDK 8+ 已弃用分段锁(Segment),改用 Node 数组 + synchronized 锁单个桶 + CAS 操作。写操作只锁冲突的链表头或红黑树根,读完全无锁,且能保证 get 看到最近一次完成的 put 结果(happens-before)。
使用场景:高并发缓存、计数器、共享状态映射表。
-
size()返回近似值,可能漏计正在写入的条目;需精确值得用mappingCount() -
keySet()返回的集合不支持remove(),否则抛UnsupportedOperationException - 遍历时若其他线程修改,不会抛异常,但可能跳过或重复某些 entry
CopyOnWriteArraySet 和 Collections.synchronizedSet 的根本区别
CopyOnWriteArraySet 底层用 CopyOnWriteArrayList 实现,每次 add 都要遍历查重 + 复制数组,O(n) 写性能;Collections.synchronizedSet(new HashSet()) 只是对所有方法加同一把 mutex,读写都串行,吞吐低但内存省。
容易踩的坑:
-
CopyOnWriteArraySet的contains()是 O(n),别在大集合里高频调用 - 两者都不保证复合操作原子性,比如“检查不存在再添加”仍需手动加锁或用
ConcurrentHashMap.computeIfAbsent -
synchronizedSet迭代时必须手动同步外部iterator,否则可能抛ConcurrentModificationException
什么时候该选 ConcurrentLinkedQueue 而不是 BlockingQueue
当不需要阻塞语义、也不需要容量限制,且要求极高吞吐的无界队列时,ConcurrentLinkedQueue 是纯 CAS 实现,无锁无等待;而 LinkedBlockingQueue 或 ArrayBlockingQueue 有锁或条件队列,适合生产者消费者节奏不匹配、需限流或等待通知的场景。
注意点:
-
ConcurrentLinkedQueue的size()是遍历计数,O(n),且结果可能瞬间过期 - 它不实现
BlockingQueue接口,没有take()、put()等阻塞方法 - 空队列调用
poll()返回null,不是阻塞,业务逻辑要显式判空
真正难处理的是跨多个集合的复合操作——比如从一个 CopyOnWriteArrayList 移元素到 ConcurrentHashMap,这种时候锁或事务语义得自己兜底,JDK 并发包不负责。










