arraylist非线程安全,多线程下易出错;推荐方案:1. collections.synchronizedlist(轻量加锁,复合操作需手动同步);2. copyonwritearraylist(读多写少场景,读无锁、写复制);3. 手动细粒度加锁(synchronized块或reentrantlock)。vector等方案不推荐。

ArrayList 本身不是线程安全的,多线程并发读写时容易出现 ConcurrentModificationException、元素覆盖、ArrayIndexOutOfBoundsException 或 size 不准确等问题。根本原因在于它的核心操作(如 add)不是原子的,且 elementData、size、modCount 都未做可见性或同步保障。
用 Collections.synchronizedList 包装
这是最轻量、最直接的改造方式,适合已有 ArrayList 逻辑、只需快速加锁的场景:
- 创建方式:
List<string> list = Collections.synchronizedList(new ArrayList());</string> - 单个方法(
add、get、size)自动加锁,线程安全 - 但复合操作仍需手动同步——例如遍历时必须显式加锁,否则会抛异常:
synchronized (list) { for (String s : list) { ... } } - 不推荐用于高频迭代+修改混合的场景,锁粒度较粗,可能成为性能瓶颈
改用 CopyOnWriteArrayList
适用于“读远多于写”的典型并发场景,比如监听器列表、配置缓存、状态订阅等:
- 读操作完全无锁,高性能;写操作(
add/remove/set)会复制整个数组并替换引用 - 迭代器基于快照,天然避免
ConcurrentModificationException,且不会阻塞读线程 - 缺点明显:写操作内存开销大、延迟高;迭代器看不到写入后的最新数据;不适合写频繁或大数据量场景
- 创建即安全:
List<string> list = new CopyOnWriteArrayList();</string>,无需额外同步
手动加锁控制关键逻辑
当业务需要原子性组合操作(如“检查存在再添加”),或已有共享 ArrayList 实例无法整体替换时,推荐细粒度加锁:
- 用
synchronized块包裹逻辑段,锁对象建议是final的专用锁实例,避免锁错对象:
private final Object lock = new Object();
synchronized (lock) { if (!list.contains(x)) list.add(x); } - 比
synchronized(list)更可靠,尤其当 list 引用可能被外部替换时 - 也可选用
ReentrantLock,支持可中断、超时、公平性等高级特性 - 注意锁范围要覆盖全部依赖共享状态的操作,避免漏锁导致竞态
不建议的方案
Vector 虽线程安全,但所有方法都用 synchronized 修饰,锁粒度太粗、性能差、API 过时,已不推荐在新项目中使用。也不应通过继承 ArrayList 并重写方法加 synchronized,破坏封装且无法保证底层字段可见性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











