ArrayList本身是线程不安全的容器,除了使用synchronized
关键字外,还有多种方式可以保证其线程安全性。以下是完整的解决方案:
一、Collections工具类包装
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
原理:
-
返回SynchronizedRandomAccessList内部类
-
所有方法都使用同步代码块锁定mutex对象
-
迭代时需要手动同步
使用示例:
// 写入时自动同步
synchronizedList.add("item");
// 遍历时需要手动同步
synchronized(synchronizedList) {
for (String item : synchronizedList) {
// 操作item
}
}
二、CopyOnWriteArrayList
CopyOnWriteArrayList<String> copyOnWriteList = new CopyOnWriteArrayList<>();
核心机制:
-
写时复制:修改操作(add/set/remove)时复制整个底层数组
-
读无锁:读操作基于不可变数组,无需同步
-
最终一致性:读操作可能读到旧数据
适用场景:
-
读多写少(如监听器列表、配置信息)
-
遍历操作远多于修改操作
性能特点:
操作 | 时间复杂度 | 锁机制 |
---|---|---|
读(get) | O(1) | 无锁 |
写(add) | O(n) | ReentrantLock |
三、ThreadLocal隔离
ThreadLocal<List<String>> threadLocalList = ThreadLocal.withInitial(ArrayList::new);
实现原理:
-
每个线程持有独立的ArrayList实例
-
完全避免多线程竞争
-
需要自行处理线程间的数据传递
适用场景:
-
线程封闭场景
-
方法调用链中的临时列表
-
不需要跨线程共享数据的情况
四、不可变集合
List<String> immutableList = List.of("a", "b", "c");
// 或
List<String> immutableList = Collections.unmodifiableList(new ArrayList<>());
-
完全禁止修改操作(add/set/remove抛出UnsupportedOperationException)
-
最安全的线程保障
-
适用于配置信息等不变数据集
五、并发队列替代方案
// 有界队列
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(100);
// 无界队列
ConcurrentLinkedQueue<String> concurrentQueue = new ConcurrentLinkedQueue<>();
适用场景:
-
生产者-消费者模式
-
需要阻塞/非阻塞操作
-
替代List作为临时缓冲区
六、性能对比
方案 | 读性能 | 写性能 | 内存开销 | 适用场景 |
---|---|---|---|---|
Collections.synchronizedList | 中 | 差 | 低 | 通用方案 |
CopyOnWriteArrayList | 优 | 差 | 高 | 读多写少 |
ThreadLocal | 优 | 优 | 中 | 线程封闭 |
不可变集合 | 优 | 不支持 | 低 | 静态数据 |
并发队列 | 中 | 中 | 中 | 生产者-消费者模式 |
七、最佳实践建议
-
读多写少:优先考虑
CopyOnWriteArrayList
// 事件监听器列表 private final CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();
-
写多读少:使用
Collections.synchronizedList
+批量操作List<String> syncList = Collections.synchronizedList(new ArrayList<>()); // 批量添加时只同步一次 synchronized(syncList) { syncList.addAll(batchData); }
-
超高并发:考虑分段锁方案(类似ConcurrentHashMap)
class SegmentList<T> { private final List<T>[] segments; private final Object[] locks; public SegmentList(int segmentCount) { // 初始化分段 } public void add(T item) { int segment = hash(item) % segments.length; synchronized(locks[segment]) { segments[segment].add(item); } } }
-
Java 9+优化:使用
List.of()
创建不可变集合private static final List<String> CONSTANT_LIST = List.of("A", "B", "C");
八、常见误区
-
错误认为volatile能保证线程安全:
// 错误示例!volatile只能保证引用可见性,不能保证集合操作原子性 private volatile ArrayList<String> list = new ArrayList<>();
-
忽略复合操作的原子性:
// 错误!contains+add不是原子操作 if (!list.contains(item)) { list.add(item); }
-
过度同步导致性能问题:
// 不必要的方法级同步 public synchronized void addAll(List<String> items) { list.addAll(items); }
选择线程安全方案时,需要根据实际场景的读写比例、性能要求和一致性需求做出权衡。对于大多数应用场景,CopyOnWriteArrayList
和Collections.synchronizedList()
是最常用的解决方案。