Vector线程安全是表面安全、实际脆弱:所有public方法用synchronized修饰,锁粒度粗;扩容因被同步方法调用而受保护;迭代器无锁且fail-fast不保证并发安全;复合操作非原子,需外部加锁。

Vector 的线程安全是“表面安全、实际脆弱”的典型代表——它靠 synchronized 方法实现同步,但锁粒度粗、组合操作不原子、迭代过程仍危险。分析它的源码,关键不是看有没有锁,而是看锁在哪、锁什么、漏了什么。
看 synchronized 落在哪些方法上
打开 JDK 1.8 的 Vector 源码(java.util.Vector),你会发现几乎所有 public 修改/访问方法都加了 synchronized:
-
add(E e)、remove(int index)、set(int index, E element) -
get(int index)、size()、isEmpty()、contains(Object o)
这些方法全部声明为 public synchronized E methodName(...),锁对象是 this。这意味着:只要有一个线程在调用任意一个方法,整个 Vector 实例就被独占,其他线程必须排队等待。
查扩容逻辑是否也受保护
Vector 的扩容发生在 ensureCapacityHelper(int minCapacity) 和 grow(int minCapacity) 中。翻看源码会发现:
- 这两个方法本身没有 synchronized,但它们只被 synchronized 方法(如 add)内部调用
- 由于调用链被锁包裹,扩容过程天然被保护,不会出现数组复制一半被中断的情况
- 但要注意:
capacityIncrement是 volatile 吗?不是——它是普通 int 字段,不过因为所有修改都在 synchronized 块内完成,所以不会出现可见性问题
重点盯住 fail-fast 迭代器的隐患
Vector 的 iterator() 和 listIterator() 返回的是内部类 Vector.Itr 和 Vector.ListItr。它们的构造方法里会记录当前 modCount 值,每次 next() 都校验是否变化。
- 问题在于:
iterator()方法本身没加锁,返回的迭代器也不带同步 - 如果线程 A 正在遍历,线程 B 同时调用
add()(它会更新modCount),A 下次调用next()就抛ConcurrentModificationException - 这不是 bug,是设计选择——fail-fast 只保证“及时报错”,不保证“并发安全”
对比 ArrayList 看出本质差异
把 Vector 和 ArrayList 的同名方法并排看:
- 结构几乎一样:都是
Object[] elementData+int size(Vector 叫elementCount) - 核心区别只有两点:所有方法加 synchronized,以及多一个
capacityIncrement字段 - ArrayList 的
add方法体里没有锁,也没有任何同步控制,因此多线程写必然出错(如 size++ 和数组赋值不同步)
换句话说:Vector 的线程安全,是靠“全量互斥”换来的,而不是靠精细同步或无锁算法。
识别被忽略的复合操作风险
即使每个方法都线程安全,多个方法连用依然可能出错。例如:
-
if (!vector.contains("x")) vector.add("x");—— 判断和添加之间存在时间窗口,别的线程可能已插入 -
int idx = vector.indexOf("y"); if (idx >= 0) vector.remove(idx);—— 查找和删除不是原子操作
这类场景在源码里没有任何防护机制,需要调用方自行在外层加锁(比如 synchronized(vector) { ... }),否则就是竞态条件。


















