Java线程安全集合类全面解析
目录
- 并发集合概述
- List线程安全实现
- Set线程安全实现
- Map线程安全实现
- Queue线程安全实现
- 总结
并发集合概述
Java提供了多种线程安全的集合类,主要分为两大类:
- 传统同步集合:通过
synchronized
关键字实现线程安全 - 并发集合:采用更高效的并发控制机制(如CAS、分段锁等)
List线程安全实现
1. Vector
- 底层结构:动态数组(类似ArrayList)
- 线程安全机制:所有方法使用
synchronized
修饰 - 缺点:性能差(全表锁)
- 使用场景:已过时,不推荐使用
// 示例代码
Vector<String> vector = new Vector<>();
vector.add("item"); // 同步方法
2. Collections.synchronizedList
- 底层结构:包装普通List
- 线程安全机制:所有方法通过同步块加锁
- 优点:兼容所有List实现
- 缺点:迭代时需要手动同步
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 遍历时需要手动同步
synchronized(syncList) {for(String item : syncList) {// 处理逻辑}
}
3. CopyOnWriteArrayList
- 底层结构:volatile数组+写时复制
- 线程安全机制:
- 读操作:无锁访问数组快照
- 写操作:ReentrantLock+数组复制
- 优点:读性能极高
- 缺点:写性能差,内存占用高
- 使用场景:读多写少(事件监听器、配置管理)
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("item"); // 加锁复制数组
String item = cowList.get(0); // 无锁读取
Set线程安全实现
1. Collections.synchronizedSet
- 底层结构:包装普通Set
- 线程安全机制:全方法同步
- 缺点:性能较差
2. CopyOnWriteArraySet
- 底层结构:基于CopyOnWriteArrayList
- 线程安全机制:同CopyOnWriteArrayList
- 使用场景:小型读多写少集合
3. ConcurrentSkipListSet
- 底层结构:跳表(SkipList)
- 线程安全机制:CAS+自旋
- 优点:有序且并发性能好
- 使用场景:需要有序的高并发Set
ConcurrentSkipListSet<String> set = new ConcurrentSkipListSet<>();
set.add("item"); // 线程安全
Map线程安全实现
1. Hashtable
- 底层结构:数组+链表
- 线程安全机制:全方法同步
- 缺点:性能差(全表锁)
- 使用场景:已过时,不推荐使用
2. Collections.synchronizedMap
- 底层结构:包装普通Map
- 线程安全机制:全方法同步
- 缺点:迭代时需要手动同步
3. ConcurrentHashMap
- 底层结构:
- JDK7:分段锁(Segment)
- JDK8:数组+链表/红黑树+CAS+synchronized
- 线程安全机制:
- 读操作:无锁(volatile读)
- 写操作:CAS+同步块(锁住链表头节点)
- 优点:高并发性能优异
- 使用场景:高并发键值存储(缓存、计数器等)
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1); // 分段锁/CAS
int value = map.get("key"); // 无锁读取
Queue线程安全实现
1. ConcurrentLinkedQueue
- 底层结构:链表+CAS
- 线程安全机制:无锁算法(CAS)
- 优点:高并发性能好
- 缺点:size()方法开销大
- 使用场景:高并发生产者消费者
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.offer("item"); // CAS操作
String item = queue.poll(); // CAS操作
2. BlockingQueue实现类
实现类 | 特点 | 使用场景 |
---|---|---|
ArrayBlockingQueue | 有界数组+ReentrantLock | 固定大小队列 |
LinkedBlockingQueue | 可选有界链表+双锁 | 通用阻塞队列 |
PriorityBlockingQueue | 优先级堆+锁 | 优先级任务调度 |
SynchronousQueue | 直接传递+双栈 | 线程池任务传递 |
DelayQueue | 优先级堆+锁 | 延时任务调度 |
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
queue.put("item"); // 阻塞写入
String item = queue.take(); // 阻塞取出
总结与选型建议
性能对比
集合类型 | 读性能 | 写性能 | 一致性 |
---|---|---|---|
同步集合 | 差 | 差 | 强 |
CopyOnWrite | 极好 | 差 | 弱 |
ConcurrentHashMap | 极好 | 好 | 弱 |
CAS无锁队列 | 极好 | 极好 | 弱 |
选型指南
-
List场景:
- 读多写少 →
CopyOnWriteArrayList
- 读写均衡 →
Collections.synchronizedList
- 读多写少 →
-
Map场景:
- 高并发 →
ConcurrentHashMap
- 需要排序 →
ConcurrentSkipListMap
- 高并发 →
-
Queue场景:
- 无界非阻塞 →
ConcurrentLinkedQueue
- 有界阻塞 →
ArrayBlockingQueue
- 延时任务 →
DelayQueue
- 无界非阻塞 →
-
Set场景:
- 小型集合 →
CopyOnWriteArraySet
- 大型并发 →
ConcurrentSkipListSet
- 小型集合 →
最佳实践
- 优先考虑并发集合而非同步集合
- 根据读写比例选择合适实现
- 注意迭代时的线程安全问题
- 高并发环境避免使用size()、containsAll()等方法
// 线程安全集合使用示例
ConcurrentHashMap<String, User> cache = new ConcurrentHashMap<>();
CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();
BlockingQueue<Task> taskQueue = new LinkedBlockingQueue<>(1000);
工作中我们需要根据实际情况来选择线程安全集合,这样可以在保证线程安全的同时获得最佳性能表现。