arraylist 本身非线程安全,应使用 collections.synchronizedlist() 包装;遍历和复合操作需手动同步;高读场景选 copyonwritearraylist,避免使用 vector。

Java 中 ArrayList 本身不提供线程安全保证,多线程直接操作会引发 ConcurrentModificationException、数据丢失或数组越界等风险。要实现线程安全包装,核心思路是**在不修改原有 ArrayList 行为的前提下,通过同步机制控制并发访问**。最常用且推荐的方式是使用 Collections.synchronizedList()。
用 Collections.synchronizedList 包装
这是标准库提供的轻量级、零依赖的线程安全包装方式:
- 调用
Collections.synchronizedList(new ArrayList()),返回一个代理 List,所有单方法调用(如add()、get()、size())自动加锁,保证原子性 - 底层使用的是被包装 ArrayList 实例自身的锁对象(即
this),所以同步块中用原始引用或包装引用效果一致 - 适合读写混合但并发压力不大、逻辑相对简单的场景
遍历时必须手动加锁
同步包装只保护单个方法,iterator() 和 for-each 是复合操作,不加锁仍会触发 fail-fast:
- 错误写法:
for (String s : syncList) { ... }—— 可能抛异常 - 正确写法:用原始 list 或 syncList 对象作为锁目标
synchronized (list) {<br> for (String s : list) { /* 安全遍历 */ }<br>}
复合操作需整体同步
像“检查后添加”“先查再删”这类逻辑,即使用了 synchronizedList,也不能拆成两步调用:
- 危险代码:
if (!list.contains(x)) list.add(x);—— 两个线程可能同时通过判断,导致重复添加 - 安全做法:把整个条件+动作包进同步块
synchronized (list) {<br> if (!list.contains("key")) {<br> list.add("key");<br> }<br>}
根据读写比例选更优替代方案
如果业务特征明显,可考虑更匹配的线程安全集合:
-
读远多于写(如配置缓存、监听器列表):用
CopyOnWriteArrayList,读操作无锁、迭代器绝对安全,但写操作复制整个数组,内存和性能开销大 -
高并发、读写均衡、需要丰富 API:考虑
ConcurrentLinkedQueue(非 List 接口)或自行封装带分段锁的结构,但通常优先评估是否真需 List 语义 - 不推荐 Vector:虽线程安全,但所有方法用 synchronized 方法修饰,粒度粗、性能差、API 过时,已不适用于现代 Java 应用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











