合普知识库
柔彩主题三 · 更轻盈的阅读体验

线程安全的ArrayList怎么做

发布时间:2025-12-13 12:04:39 阅读:298 次

ref="/tag/413/" style="color:#E3A3CF;font-weight:bold;">线程安全的ArrayList怎么做

在多线程环境下操作集合类时,普通 ArrayList 很容易出问题。比如你写了个抢票系统,多个用户同时下单,数据被反复修改,最后出现重复订单或者数量错乱,这多半是因为 ArrayList 不是线程安全的。

为什么 ArrayList 不安全?

ArrayList 内部用数组存储元素,当添加元素超过容量时会自动扩容。这个过程不是原子操作。多个线程同时 add() 时,可能两个线程读到同一个 size 值,导致数据覆盖或数组越界。

用 Collections.synchronizedList 包装

最简单的方式是用工具类把 ArrayList 包一下:

import java.util.*;

List<String> list = Collections.synchronizedList(new ArrayList<>());

这样每次调用 get、add 等方法都会加锁,保证线程安全。但遍历时仍需手动同步:

synchronized (list) {
    for (String item : list) {
        System.out.println(item);
    }
}

使用 CopyOnWriteArrayList

这是专门用于并发场景的 List 实现。它在修改时复制整个底层数组,读操作不加锁:

import java.util.concurrent.CopyOnWriteArrayList;

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("用户1");
list.add("用户2");

for (String user : list) {
    System.out.println(user);
}

适合读多写少的场景,比如监听器列表、配置更新。写操作频繁的话性能较差,因为每次都要复制数组。

自己加锁控制

如果需要复杂操作,比如先查后删,可以用 synchronized 或 ReentrantLock:

List<String> list = new ArrayList<>();

synchronized(list) {
    if (!list.contains("key")) {
        list.add("key");
    }
}

这种方式灵活,但要注意锁的粒度,太大会影响性能,太小可能漏掉保护。

实际选择建议

如果你的应用是高频读取、偶尔写入,比如缓存名单、日志记录,直接上 CopyOnWriteArrayList 最省心。要是写操作也多,而且逻辑复杂,推荐用 synchronizedList 并注意迭代时加锁。别为了“高并发”硬上复杂方案,大多数业务系统其实用 synchronized 就够了。