arraylist线程不安全,因其add非原子、扩容不可重入、modcount迭代器机制仅fail-fast;常用安全方案为collections.synchronizedlist、copyonwritearraylist和vector。
arraylist 本身不加任何同步控制,所有方法(如 add、remove、get)都直接操作底层数组和 size 字段,没有锁或 volatile 修饰。多个线程同时调用时,容易出现写覆盖、size 错乱、扩容冲突、迭代异常等问题。
为什么非线程安全?关键在三处
• add 操作不是原子的:读取 size → 检查容量 → 赋值到 elementData[size] → 更新 size,四步之间可能被其他线程打断。两个线程读到相同 size,就会往同一索引写入,后写的覆盖前写的。
• 扩容过程不可重入:grow() 创建新数组、复制元素、替换引用,若两线程同时触发,可能一个线程复制完,另一个线程又复制并覆盖了引用,导致部分数据丢失。
• modCount 和迭代器失效机制:迭代器依赖 modCount 做快速失败检查。并发修改时,一个线程改了 modCount,另一个线程还在用旧的 expectedModCount,立刻抛 ConcurrentModificationException —— 这不是保护数据,而是暴露问题。
三种常用安全包装方式
• Collections.synchronizedList():返回一个同步代理,所有 public 方法加了 synchronized(等价于方法级锁)。用法简单:List<string> list = Collections.synchronizedList(new ArrayList());</string>。但注意:遍历时必须手动同步整个块,否则仍会报并发修改异常。
• CopyOnWriteArrayList:JUC 提供的读写分离实现。读操作无锁,写操作加锁 + 复制整个数组 + 替换引用。适合读多写少场景(如监听器列表、配置快照),写性能差、内存开销大,但迭代绝对安全,不会抛 CME。
• Vector(不推荐新项目使用):早期线程安全类,每个方法都加 synchronized。粒度粗、性能低、已基本被 CopyOnWriteArrayList 或显式锁替代。仅用于兼容老代码。
选型建议看场景
• 如果只是临时共享、线程间不频繁修改,加 synchronized(list) 块控制临界区更轻量;
• 如果是缓存、白名单、事件监听等读远大于写的结构,优先选 CopyOnWriteArrayList;
• 如果写操作也频繁且要求强一致性,考虑 ConcurrentHashMap 替代 List 逻辑,或用显式 ReentrantLock + 普通 ArrayList 自定义封装。











