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 就够了。