for-each遍历Vector不保证线程安全,因其本质是多步非原子操作;仅当遍历期间无结构性修改时才安全,推荐使用快照遍历、CopyOnWriteArrayList或显式同步。

for-each 语法糖本身不提供线程安全保证,它只是编译器生成的遍历结构。在 Vector 等“线程安全集合”上使用 for-each,并不意味着遍历过程天然线程安全——关键要看**遍历期间是否有其他线程修改集合结构**。
Vector 的“线程安全”仅限单个操作,不覆盖遍历全程
Vector 的 add、remove、get 等方法是 synchronized 的,但 for-each 遍历本质是多步操作:先调用 iterator() 获取迭代器,再反复调用 hasNext() 和 next()。这三步之间没有原子性保护。
典型风险场景:
- 线程 A 执行 for (String s : vector) { ... },刚取完第 3 个元素;
- 线程 B 此时调用 vector.remove(0),导致内部数组收缩、元素前移;
- 线程 A 继续 next() 时可能跳过元素、重复访问,甚至抛出 ConcurrentModificationException(尽管 Vector 未使用 fail-fast 检查机制,但某些 JDK 实现或包装类仍可能触发)。
哪些 Vector 遍历方式相对可控?
不是所有 for-each 都等价。需区分实际行为边界:
- 只读遍历 + 无并发修改:若能确保遍历期间无任何线程调用 add/remove/insertElementAt 等结构性修改方法,则 for-each 在 Vector 上可安全使用;
- 遍历时只读取字段(非修改对象状态):如 for (User u : vector) { System.out.println(u.name); } —— 这不改变 Vector 结构,也不影响 u 实例的线程可见性(需 u 自身字段为 volatile 或 final);
- 避免混用 Enumeration 和 for-each:Vector.elements() 返回 Enumeration,与 for-each 底层的 Iterator 不共享状态,交叉使用易引发不可预测行为。
真正需要线程安全遍历的推荐做法
不要依赖 Vector 的同步粒度来掩盖遍历逻辑缺陷。更健壮的实践包括:
-
快照遍历:在同步块内复制一份快照再遍历:synchronized (vector) { List
snapshot = new ArrayList(vector); } for (String s : snapshot) { ... } ; - 使用 java.util.concurrent 类型:如 CopyOnWriteArrayList —— 其 for-each 天然安全,因为迭代器基于创建时刻的数组快照,不受后续 add/remove 影响;
-
显式同步 + 迭代器 remove:若必须边遍历边删,改用 synchronized 块包裹 Iterator 并调用 remove():synchronized (vector) { Iterator
it = vector.iterator(); while (it.hasNext()) { if (shouldRemove(it.next())) it.remove(); } } ; - 避免 Vector,改用 Collections.synchronizedList(new ArrayList()):它明确要求所有遍历操作也必须在外部同步,强制暴露线程安全契约,不易误用。
一个容易被忽略的陷阱:for-each + 修改对象属性 ≠ 安全
例如:for (AtomicInteger ai : vector) { ai.incrementAndGet(); }
这看似没动 Vector 结构,但若多个线程同时执行该循环,AtomicInteger 的操作是线程安全的,而 Vector 本身不保证其元素引用的发布安全(如某线程刚 new 出 AtomicInteger 放入 Vector,另一线程就遍历到它,可能看到未完全构造的对象)。应确保元素对象的构造和发布满足安全发布原则(如通过 final 字段、volatile 引用或同步块)。

















