java迭代器非线程安全,应避免共享,改用copyonwritearraylist等并发集合的弱一致性迭代器、为每线程创建独立迭代器,或用atomicinteger手动索引遍历。

Java 迭代器本身不是线程安全的,它的游标(cursor)状态保存在单个迭代器实例内部,多线程共享同一个迭代器时,游标会被多个线程交叉修改,导致重复、跳过、越界甚至 ConcurrentModificationException。所以“安全控制游标”不是靠给迭代器加锁或同步 next(),而是**避开共享迭代器**,改用线程安全的遍历策略。
用线程安全集合自带的迭代器
像 CopyOnWriteArrayList 这类并发集合,其 iterator() 返回的迭代器是弱一致性的:它基于创建时刻的数组快照工作,游标推进完全独立于其他线程的写操作。
- 迭代过程中,其他线程增删元素不会影响当前遍历——不会抛异常,也不会看到新插入/删除的元素
- 适合读多写少场景,比如配置列表、监听器注册表等
- 注意:不能用于需要实时反映最新数据的强一致性场景
为每个线程分配独立迭代器
不要把一个迭代器对象传给多个线程。正确做法是让每个线程自己调用集合的 iterator() 方法获取专属副本:
- 对
ArrayList、LinkedList等普通集合,每个线程持有一个自己的迭代器,互不干扰 - 若需分片处理(如 8 个线程各处理 1/8 的数据),可配合
subList()或下标计算,再各自创建迭代器 - 避免用“全局迭代器 + synchronized 块”包装 next()——这会严重串行化,失去并发意义
用原子索引替代游标,手动控制遍历
放弃迭代器,转而用线程安全的原子计数器协调访问:
- 声明
AtomicInteger cursor = new AtomicInteger(0) - 每个线程循环调用
cursor.getAndIncrement()获取唯一索引 - 用该索引从线程安全集合(如
CopyOnWriteArrayList)或不可变列表中取元素 - 需自行判断越界(
index ),但逻辑清晰、无状态竞争
批量预分配任务,彻底隔离游标状态
最稳妥的方式是提前把数据切分成互斥子集,分发给线程:
- 主线程将大集合按线程数拆成 N 个子列表(如用 Guava 的
Lists.partition()) - 每个子列表交给一个线程,线程内用普通迭代器遍历,完全无共享
- 适用于静态数据或写操作极少的场景,吞吐高、无锁、无异常风险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











