arraylist线程不安全,因size更新非原子且扩容过程未同步;vector和collections.synchronizedlist提供方法级同步但性能差;高并发应选copyonwritearraylist等专用并发容器。

ArrayList 非线程安全,核心在于它所有修改操作(如 add、remove、set)都没有同步控制。多线程同时调用时,可能造成数据覆盖、数组越界、ConcurrentModificationException 或最终 size 小于预期值——这不是偶发 Bug,而是设计使然。
ArrayList 线程不安全的两个关键原因
一是 size 更新非原子:比如 elementData[size++] = e 实际拆成两步——先赋值、再自增。两个线程若同时走到这一步,可能都写进 elementData[9],其中一个被覆盖。
二是 扩容过程未受保护:当多个线程发现容量不足,都触发 grow(),但只有一方完成扩容并更新底层数组引用;另一方仍按旧数组长度操作,写入时就抛 ArrayIndexOutOfBoundsException。
用 Vector 替代 ArrayList 的实际效果
Vector 在几乎所有 public 方法(add、get、remove 等)上加了 synchronized,保证同一时刻只有一个线程能执行这些操作。
但要注意:
- 锁是方法级的,粒度粗,吞吐量低,高并发下容易成为瓶颈
- 默认扩容为原容量 2 倍,比 ArrayList 的 1.5 倍更激进,内存占用更高
- 支持
Enumeration和Iterator,但遍历时仍需注意 fail-fast 行为
Collections.synchronizedList 包装的用法与限制
这是对现有 ArrayList 的轻量改造方式:
List<string> safeList = Collections.synchronizedList(new ArrayList());</string>
它返回一个代理对象,所有方法调用都会被 synchronized 同步——但仅限单个方法调用。
关键限制:
- 迭代必须手动加锁,否则遍历中其他线程修改会抛异常
- 复合操作(如“检查是否存在再添加”)仍需外部同步,因为
contains()和add()是两次独立调用 - 底层仍是 ArrayList,扩容逻辑、内存效率等不变
正确遍历示例:
synchronized (safeList) {
for (String s : safeList) {
System.out.println(s);
}
}
真正适合高并发场景的替代选择
Vector 和 synchronizedList 解决的是“能用”,不是“好用”。在真实高并发系统中,更推荐:
- CopyOnWriteArrayList:读多写少场景(如监听器列表、配置快照),写操作加锁+复制数组,读完全无锁
- ConcurrentLinkedQueue 或 BlockingQueue 子类:适用于队列型结构,避免 List 的随机访问需求
- 分段加锁或业务层隔离:例如按 key 分桶,每个桶用独立 ArrayList + ReentrantLock,降低锁竞争
Vector 和包装方案可以快速兜底,但别把它当成高并发下的长期解法。











