【Java】如何保证集合的线程安全?
【Java】如何保证集合的线程安全?
文章目录
- 1. CopyOnWriteArrayList
 - 2. ConcurrentHashMap
 - 3. Collections.synchronizedList
 - 4. 其他线程安全集合
 - BlockingQueue 系列
 - ConcurrentLinkedQueue
 
- 5. 性能对比和使用建议
 - 6. 实际应用示例
 
1. CopyOnWriteArrayList
适用场景:读多写少的并发场景
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteExample {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();// 写操作 - 会复制整个底层数组list.add("A");list.add("B");// 读操作 - 无需加锁,性能高for (String item : list) {System.out.println(item); // 线程安全地遍历}// 批量操作list.addAll(List.of("C", "D"));}
}
 
特点:
- 写操作时复制整个数组,开销大 ; 复制新数组 → 修改新数组 → 替换引用
 - 读操作不需要锁,性能极高 ; 直接访问当前数组,无锁
 - 迭代器不会抛出
ConcurrentModificationException; 基于创建时的快照,不会看到后续修改 - 数据最终一致性,不保证实时一致性
 
2. ConcurrentHashMap
适用场景:高并发键值对存储
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 基本操作map.put("key1", 1);map.putIfAbsent("key1", 2); // 如果不存在则插入// 原子操作map.compute("key2", (k, v) -> v == null ? 1 : v + 1);// 线程安全的遍历map.forEach((k, v) -> System.out.println(k + ": " + v));// 搜索操作Integer result = map.search(1, (k, v) -> v > 0 ? k : null);}
}
 
特点:
- 分段锁(Java 7)或 CAS + synchronized(Java 8+); 每个桶独立加锁
 - 高并发读写性能优秀 ; 通常无锁
 - 不会锁住整个Map; 只锁单个桶或使用CAS
 - 提供原子操作方法(volatile读取)
 
3. Collections.synchronizedList
适用场景:需要将现有集合转换为线程安全版本
import java.util.*;public class SynchronizedCollectionExample {public static void main(String[] args) {// 包装普通ArrayListList<String> syncList = Collections.synchronizedList(new ArrayList<>());// 基本操作 - 自动加锁syncList.add("A");syncList.add("B");// 遍历时需要手动同步synchronized(syncList) {Iterator<String> iterator = syncList.iterator();while(iterator.hasNext()) {System.out.println(iterator.next());}}// 同样适用于其他集合Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());}
}
 
特点:
- 在所有方法上加 synchronized 锁 ; 所有操作共用一把锁
 - 性能相对较低 ; 方法级别同步
 - 遍历时需要手动同步
 - 适合低并发场景 ; 高并发时竞争激烈
 
4. 其他线程安全集合
BlockingQueue 系列
import java.util.concurrent.*;public class BlockingQueueExample {public static void main(String[] args) throws InterruptedException {// 有界队列BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);// 生产者queue.put("item1"); // 阻塞直到有空间queue.offer("item2", 1, TimeUnit.SECONDS); // 超时等待// 消费者String item = queue.take(); // 阻塞直到有元素item = queue.poll(1, TimeUnit.SECONDS); // 超时等待}
}
 
ConcurrentLinkedQueue
import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentQueueExample {public static void main(String[] args) {ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();// 无界非阻塞队列queue.offer("A");queue.offer("B");String item = queue.poll(); // 非阻塞}
}
 
5. 性能对比和使用建议
| 集合类型 | 适用场景 | 性能特点 | 一致性 | 
|---|---|---|---|
CopyOnWriteArrayList | 读多写少 | 读快写慢 | 最终一致 | 
ConcurrentHashMap | 高并发K-V | 读写都快 | 弱一致 | 
Collections.synchronizedList | 低并发 | 读写都慢 | 强一致 | 
BlockingQueue | 生产者消费者 | 阻塞操作 | 强一致 | 
6. 实际应用示例
public class ThreadSafeCache {private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();private final CopyOnWriteArrayList<String> accessLog = new CopyOnWriteArrayList<>();public void put(String key, Object value) {cache.put(key, value);accessLog.add("PUT: " + key);}public Object get(String key) {accessLog.add("GET: " + key);return cache.get(key);}// 批量获取 - 线程安全public Map<String, Object> getAll(Set<String> keys) {Map<String, Object> result = new HashMap<>();for (String key : keys) {Object value = cache.get(key);if (value != null) {result.put(key, value);}}return result;}
}
 
选择建议:
- 高并发读:
CopyOnWriteArrayList; 空间换时间,适合很少修改的读密集型场景 - 高并发K-V:
ConcurrentHashMap; 分段锁/CAS,适合高并发读写 - 简单同步:
Collections.synchronizedXxx; 全局锁,简单但性能较差 - 任务队列:
BlockingQueue - 无锁队列:
ConcurrentLinkedQueue 
