arraylist和vector都可能抛concurrentmodificationexception,但arraylist极易发生,vector因方法同步而概率极低;核心差异在于vector提供方法级同步保护,arraylist完全无锁,需开发者自行协调并发。

ArrayList 和 Vector 在并发读写时的行为差异,核心不在“是否抛异常”,而在于“何时抛、为何抛、能否避免”。它们都可能抛 ConcurrentModificationException,但触发条件和底层机制不同。
ArrayList 并发修改异常的根源
ArrayList 本身非线程安全,其迭代器(Iterator)内部维护一个 expectedModCount 字段,用于记录创建迭代器时集合的修改次数(modCount)。只要在迭代过程中有其他线程或同一线程调用 add、remove 等结构性修改方法,modCount 就会变化,下次调用 next() 或 remove() 时就会检查不一致并抛出 ConcurrentModificationException。
- 即使单线程中边遍历边删除,也会触发该异常(常见误区)
- 只读操作(如
get()、contains())不会改变modCount,因此并发读不直接引发异常 - 异常本质是“快速失败(fail-fast)”机制,用于尽早暴露逻辑错误,而非并发控制手段
Vector 并发读写时的异常表现
Vector 的所有公共方法(如 add()、get()、iterator())默认加了 synchronized,保证了单个方法调用的原子性。但这不等于整个复合操作线程安全——比如“先检查再插入”这类操作仍需外部同步。
- 它的迭代器也实现 fail-fast,同样会比对
modCount与expectedModCount - 但由于方法同步,多个线程无法真正“同时”修改结构,所以
ConcurrentModificationException实际出现概率远低于 ArrayList - 更典型的问题是:并发读写虽不常抛 CME,却可能导致数据覆盖、丢失或逻辑错乱(例如两个线程同时
add(),结果只成功一个)
关键区别不是“抛不抛”,而是“有没有保护”
Vector 提供的是方法级同步,相当于给每个操作上了一把独占锁;ArrayList 完全无锁,靠开发者自行协调。这意味着:
- Vector 在高并发下性能差(锁粒度粗),但不容易因并发遍历+修改而崩溃
- ArrayList 性能好,但必须配合正确并发策略(如使用
CopyOnWriteArrayList、显式加锁、或仅用迭代器remove())才能安全使用 - 两者都可能抛
ConcurrentModificationException,但 Vector 更多是“理论可能”,ArrayList 是“极易发生”
实际开发中的建议
不要依赖 Vector 的同步来构建并发逻辑。现代 Java 推荐:
- 单线程场景:首选 ArrayList,指定初始容量减少扩容开销
- 需要线程安全且读多写少:用
CopyOnWriteArrayList(迭代安全,写操作复制数组) - 需要强一致性且复杂操作:用
Collections.synchronizedList(new ArrayList()),但记得手动同步迭代块 - 避免在循环中直接调用
list.remove(obj)或list.add()—— 无论 ArrayList 还是 Vector,都应改用迭代器remove()或收集待删元素后批量处理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











