在开发多ref="/tag/413/" style="color:#C468A7;font-weight:bold;">线程程序时,经常会遇到多个线程同时操作同一个集合的情况。比如一个打印任务调度系统,多个线程往任务队列里添加待打印的文件,这时候如果用普通的 ArrayList,很容易出现数据错乱甚至程序崩溃。
ArrayList 本身不是线程安全的,多个线程同时 add 或 get 的时候,可能触发并发修改异常(ConcurrentModificationException),或者读到不一致的数据。那怎么解决?
使用 Collections.synchronizedList
最简单的方式是用工具类包装一下:
import java.util.*;
List<String> list = Collections.synchronizedList(new ArrayList<>());
list.add("打印合同");
list.add("打印简历");
这样得到的 list 是线程安全的,但要注意:遍历时仍需手动同步:
synchronized (list) {
for (String task : list) {
System.out.println("正在处理:" + task);
}
}
使用 CopyOnWriteArrayList
这是专门用于并发场景的 List 实现,适合读多写少的场合。比如打印系统的监控界面要频繁读取当前任务列表,但新增任务的频率不高。
import java.util.concurrent.CopyOnWriteArrayList;
CopyOnWriteArrayList<String> tasks = new CopyOnWriteArrayList<>();
tasks.add("打印发票");
tasks.add("打印清单");
for (String task : tasks) {
System.out.println(task); // 无需加锁
}
它的原理是每次修改都创建新副本,读操作完全无锁,效率高,但频繁写会消耗内存和性能。
使用 Vector(不推荐)
Vector 是老式的线程安全集合,所有方法都加了 synchronized,性能较差,现在基本不用了。
如果你在做一个小型局域网打印共享程序,多个用户提交任务,建议直接上 CopyOnWriteArrayList,代码简洁又不容易出错。要是写的是高性能后台服务,对写操作要求高,可以考虑配合 synchronized 块使用普通 ArrayList 或转用其他并发结构。