# 问题
5. Vector 为什么被淘汰?它的 synchronized 影响了什么?
# 标准答案
Vector
在 Java 1.0 版本引入,提供了线程安全的动态数组,但由于所有方法都加了 synchronized,导致并发性能低下。在多线程环境下,Vector
的同步机制会引发性能瓶颈、锁竞争,甚至可能影响 CPU 缓存效率。Java 1.2 之后,ArrayList
取代了 Vector
,如果需要线程安全的动态数组,推荐使用 Collections.synchronizedList(new ArrayList<>())
或 CopyOnWriteArrayList
。
# 答案解析
# 1. Vector
的同步机制
Vector
是 Java 1.0 设计的线程安全集合类,它的 核心方法全部使用 synchronized
关键字,例如:
public synchronized boolean add(E e) {
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 所有方法都加了
synchronized
,意味着每次增删改查都必须获取锁,即使没有竞争,也会有额外的锁开销。 - 线程争抢锁时会导致上下文切换,在高并发环境下,大量线程竞争
Vector
的锁,容易产生锁膨胀(Lock Contention),影响吞吐量。
# 2. Vector
的 synchronized 影响了什么?
# (1)降低并发性能
Vector
采用 “方法级同步”,即整个方法加锁,但:
- 锁粒度太大:即使多个线程访问不同的索引,也会被同步限制,导致性能下降。
- 串行执行:多个线程不能并发访问
Vector
,导致吞吐量低。 - 不支持局部并发:比如
for
循环遍历时,每次get()
仍然需要获取锁,即使Vector
只读,也会阻塞其他线程。
# (2)影响 CPU 缓存
synchronized
可能导致 CPU 缓存行失效,多线程频繁访问Vector
时,多个 CPU 核心间需要不断同步数据,影响缓存命中率。- 现代 CPU 多核架构下,锁的粒度越细,性能越高,但
Vector
的全局锁设计导致缓存一致性成本高。
# (3)多线程访问开销大
Vector
即使在单线程下,synchronized
依然会带来额外的锁检查、锁升级等开销。- 在 高并发环境下,锁竞争严重时,可能引发
Thread contention
(线程争用),进而导致 GC 频繁发生,影响系统性能。
# 3. 为什么 ArrayList
取代了 Vector
?
对比项 | Vector (已淘汰) | ArrayList (推荐) |
---|---|---|
线程安全 | 是(全局锁) | 否(非线程安全) |
同步方式 | synchronized | 无 |
并发性能 | 低(锁竞争严重) | 高(无锁访问) |
遍历效率 | 低(每次 get() 都加锁) | 高(直接数组索引) |
替代方案 | CopyOnWriteArrayList / Collections.synchronizedList | ArrayList |
# 4. 如果要线程安全的 List
,应该用什么?
# 方案 1:Collections.synchronizedList()
如果只是简单地想让 ArrayList
线程安全,可以用:
List<String> list = Collections.synchronizedList(new ArrayList<>());
1
但:
- 只能保证方法级别的线程安全,不能保证迭代时的安全,需要手动加锁:
synchronized (list) { for (String s : list) { System.out.println(s); } }
1
2
3
4
5 - 适用于 低并发场景,高并发建议用
CopyOnWriteArrayList
。
# 方案 2:CopyOnWriteArrayList
如果是读多写少的场景(如缓存、黑名单查询等),推荐 CopyOnWriteArrayList
:
List<String> list = new CopyOnWriteArrayList<>();
1
- 写操作时复制一份数据,避免锁竞争,适合读多写少的应用,如 配置缓存、订阅列表等。
- 但写入性能较低(O(n) 复制成本),不适用于高频写操作的场景。
# 方案 3:并发环境下的 ConcurrentLinkedQueue
如果是生产者-消费者模型,使用 ConcurrentLinkedQueue
可能比 Vector
更好:
Queue<String> queue = new ConcurrentLinkedQueue<>();
queue.offer("task1");
String task = queue.poll();
1
2
3
2
3
- 无锁(CAS)实现,适合高并发环境。
- 适用于任务队列、异步消息处理等场景。
# 深入追问
# 1. 为什么 Vector
的全局锁会降低 CPU 并发性能?
- 全局锁(
synchronized
)导致串行化,多个线程同时调用Vector.add()
时,只有一个线程能持有锁,其他线程都需要等待。 - CPU 上下文切换开销大,频繁锁竞争会导致 线程频繁阻塞、上下文切换(Context Switch),进而影响吞吐量。
# 2. CopyOnWriteArrayList
为什么适合读多写少?
- 写入时复制整个数组,避免了
synchronized
的锁竞争。 - 但 复制成本高(O(n)),不适用于高频写入的场景。
# 3. 为什么 Collections.synchronizedList()
不能完全取代 Vector
?
Collections.synchronizedList()
只对方法级别加锁,但不能保证迭代时的安全,仍然需要手动加锁。- 适用于低并发场景,但高并发仍然推荐
CopyOnWriteArrayList
。
# 相关面试题
ArrayList
为什么比Vector
更高效?CopyOnWriteArrayList
为什么写入慢?适用于什么场景?ConcurrentLinkedQueue
和CopyOnWriteArrayList
在并发场景下如何选择?synchronized
的锁优化机制(偏向锁、轻量级锁、重量级锁)如何影响Vector
?