arraylist非线程安全,需依读写模式选方案:读多写少用unmodifiablelist;强一致性用显式同步或并发容器;最终一致可用copyonwritearraylist;须封装访问入口并控制锁粒度。

ArrayList 本身不是线程安全的,多个线程同时读写时,可能引发脏读(读到未完成写入的中间状态)或元素覆盖(如 add() 过程中扩容与写值交错),甚至抛出 ConcurrentModificationException。要真正解决问题,不能只靠“加锁”或“换集合”,而应结合场景选对方案。
明确访问模式:读多写少?读写频次接近?是否需实时一致性?
这是选型前提。例如:
- 若只是多个线程频繁读、极少写(如配置列表初始化后只读),用
Collections.unmodifiableList(new ArrayList(...))包一层,既轻量又安全; - 若读写都较频繁,且要求强一致性(如库存计数),必须引入显式同步或使用线程安全容器;
- 若允许最终一致(如日志缓冲区),可考虑无锁结构如
CopyOnWriteArrayList,但注意它写操作复制整个数组,大数据量时开销大。
写操作集中管控:用 ReentrantLock 或 synchronized 封装关键段
比直接替换容器更灵活,尤其适合已有逻辑复杂、不能轻易改集合类型的情况。关键点是锁粒度要准:
- 不要锁整个方法,只锁真正共享修改的代码块,比如仅包裹
list.add()和list.remove(); - 避免在锁内做耗时操作(如网络调用、文件读写),否则会严重拖慢并发性能;
- 若涉及多个集合协同更新(如 map + list 联动),确保用同一把锁,防止锁分离导致状态不一致。
优先考虑并发容器:ConcurrentHashMap 配合 CopyOnWriteArrayList 或 BlockingQueue
Java 并发包提供了更精细的控制:
-
CopyOnWriteArrayList适合读远多于写的场景(如监听器列表),add/remove 时复制数组,读完全无锁; - 若需线程安全的队列行为(如生产者-消费者),
BlockingQueue(如LinkedBlockingQueue)比手动同步 ArrayList 更可靠; - 若数据本质是键值映射,别硬套 ArrayList,改用
ConcurrentHashMap,再通过keySet()或values()视图获取动态列表——多数时候这才是真实需求。
避免“伪线程安全”:synchronized(list) 不等于安全
常见误区是简单写 synchronized (list) { list.add(...); },但问题没解决:
- 其他线程仍可能绕过该锁,直接调用 list 的方法;
- 迭代时即使加了锁,若另一线程在迭代中途修改,仍可能触发
ConcurrentModificationException(fail-fast 机制); - 正确做法是封装访问入口(如自定义 ListWrapper 类),所有增删查都走带锁方法,并禁止外部直接持有原始 list 引用。
不复杂但容易忽略:线程安全从来不是容器单方面的责任,而是访问方式、封装边界和业务语义共同决定的。选对工具,守住入口,才能稳住数据。










