ConcurrentSkipListSet的Iterator是弱一致性(weakly consistent)而非fail-fast或fail-safe;它不抛ConcurrentModificationException,不复制快照,而是通过volatile读、CAS更新和节点标记机制,在遍历中容忍并发修改并保证安全导航。

ConcurrentSkipListSet 的 Iterator 并不基于 fail-safe 机制,而是基于 weakly consistent(弱一致性)迭代器设计,它既不是 fail-fast 也不是传统意义上的 fail-safe。
ConcurrentSkipListSet 的 Iterator 不是 fail-safe
“fail-safe” 通常指迭代器在遍历时对底层集合的修改完全不可见(如 CopyOnWriteArrayList 的迭代器,内部持有快照),但 ConcurrentSkipListSet 的迭代器并非如此。它不复制数据,也不冻结状态,而是在遍历过程中容忍并发修改,并尽可能反映“近实时”的结构视图。
- 底层基于 ConcurrentSkipListMap,其迭代器实现于
ConcurrentSkipListMap.KeyIterator等内部类 - 迭代过程通过跳表的层级链表结构逐节点推进,依赖 volatile 引用和 CAS 操作保证可见性
- 不会抛出
ConcurrentModificationException(所以不是 fail-fast) - 也不保证看到所有已添加/未删除的元素(所以不是严格意义上的 fail-safe)
弱一致性(Weakly Consistent)的核心行为
Java Doc 明确将 ConcurrentSkipListSet 的迭代器归类为 weakly consistent:它可容忍并发修改,不抛异常,不保证原子快照,但能保证遍历中不会出现重复、丢失或结构破坏(如空指针崩溃)。
- 不阻塞写操作:add/remove/contains 等方法与迭代可同时进行,互不影响性能
- 可能跳过新插入元素:若元素在当前迭代位置之后插入,且尚未被当前遍历路径覆盖,则可能不可见
- 可能包含已删除元素:若节点在迭代器访问后、next() 返回前被逻辑删除(标记为 deleted),仍可能返回该元素(取决于遍历节奏)
- 不会抛异常、不会死锁、不会读到损坏链表:所有访问均通过 volatile 读 + 向下/向右指针安全导航实现
底层跳表结构如何支撑弱一致性
ConcurrentSkipListMap 使用无锁跳表(lock-free skip list),每个节点包含多个 level 的 next 引用(均为 volatile),插入/删除通过 CAS 原子更新指针。
立即学习“Java免费学习笔记(深入)”;
- 迭代器从 head 节点开始,按 level 0 链表顺序遍历;每一步都重新读取当前节点的 next 引用(volatile 读)
- 节点删除时,并非立即移除物理内存,而是先 CAS 将其 key 设为 null、value 设为
Boolean.FALSE,再 CAS 断开前后指针(即“逻辑删除 + 物理断链”两阶段) - 迭代器遇到 key == null 的节点会自动跳过,从而避免返回已删除项 —— 这是弱一致性的关键保障之一
- 新增节点插入时,仅需 CAS 更新前驱节点的 next 指针;迭代器若已越过前驱,则新节点自然不可见
与真正 fail-safe(如 CopyOnWriteArrayList)的本质区别
fail-safe 迭代器本质是“隔离”,而 ConcurrentSkipListSet 迭代器本质是“协作”。两者解决并发问题的哲学不同:
- CopyOnWriteArrayList:迭代期间复制整个数组,写操作另起副本,内存开销大、适合读多写少
- ConcurrentSkipListSet:迭代与修改共享同一份跳表结构,靠 volatile/CAS/节点标记实现无锁协调,内存高效、适合高并发读写混合场景
- 前者保证“遍历期间看到的是某一时刻的完整快照”,后者只保证“遍历过程安全、结果合理、不抛异常”


















