Java中ArrayList线程不安全,推荐用Collections.synchronizedList包装(需手动同步迭代)、CopyOnWriteArrayList(读多写少)或显式ReadWriteLock封装;禁用继承加synchronized的反模式。

Java 中 ArrayList 本身不是线程安全的,若需在多线程环境下安全使用,不能直接加锁操作原始对象,而应通过包装或替代方案实现线程安全。最常用且推荐的方式是使用 Collections.synchronizedList() 包装,但需注意其局限性;更优的场景可考虑 CopyOnWriteArrayList 或显式加锁控制。
用 Collections.synchronizedList 包装(基础方案)
这是最直接的“包装”方式,返回一个同步代理列表,所有公共方法都加了 synchronized 锁:
List<String> list = Collections.synchronizedList(new ArrayList<>());
⚠️ 注意:虽然单个操作(如 add()、get())是线程安全的,但复合操作(如迭代、条件更新)仍需手动同步:
- 遍历时必须手动同步底层对象,否则可能抛
ConcurrentModificationException或读到不一致状态 - 正确写法:
synchronized (list) { Iterator<String> it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); } }
用 CopyOnWriteArrayList 替代(适合读多写少)
它不是“包装” ArrayList,而是独立实现,内部写操作时复制整个数组,读操作无锁:
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
List<String> list = new CopyOnWriteArrayList<>();
适用场景:
- 读操作远多于写操作(如监听器列表、配置快照)
- 写操作不频繁,且能接受写操作的开销和弱一致性(迭代器看不到最新 add/remove)
- 无需手动同步,迭代安全,无需额外
synchronized块
手动包装 + 显式锁(精细控制)
若需自定义同步粒度(如只锁部分逻辑、读写分离),可封装一个带 ReentrantLock 或 ReadWriteLock 的列表类:
public class SafeArrayList<E> {
private final List<E> delegate = new ArrayList<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public void add(E e) {
lock.writeLock().lock();
try { delegate.add(e); }
finally { lock.writeLock().unlock(); }
}
public E get(int index) {
lock.readLock().lock();
try { return delegate.get(index); }
finally { lock.readLock().unlock(); }
}
}
优势:
- 读操作可并发,提升吞吐量
- 可按需控制锁范围,避免
synchronizedList的全局锁瓶颈 - 便于扩展原子操作(如
addIfAbsent())
不推荐的方式:继承 ArrayList 并加 synchronized 方法
例如重写 add() 加 synchronized,这无法保证迭代、批量操作等组合行为的安全性,且破坏封装,容易遗漏关键方法,属于反模式,应避免。
选择依据主要看读写比例、实时性要求和是否需要复合原子操作。多数简单场景用 Collections.synchronizedList 足够;高并发读+低频写优先选 CopyOnWriteArrayList;复杂同步逻辑建议封装并使用显式锁。

















