# 问题

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
  • 所有方法都加了 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
  • 无锁(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 为什么写入慢?适用于什么场景?
  • ConcurrentLinkedQueueCopyOnWriteArrayList 在并发场景下如何选择?
  • synchronized 的锁优化机制(偏向锁、轻量级锁、重量级锁)如何影响 Vector