一、并发安全问题根源
1. List(如ArrayList)
- 问题表现 :多线程同时调用add、remove等方法时,可能抛出ConcurrentModificationException或导致数据不一致。
- 根本原因 :
- 非原子性操作 :如add操作的流程(检查容量→扩容→插入)在多线程下可能被中断。
- 结构性修改冲突:多线程同时修改集合结构(如扩容)导致内部数组索引混乱。
 
- 非原子性操作 :如
2. Set(如HashSet)
- 问题表现:HashSet底层基于HashMap,多线程插入可能丢失数据或触发异常。
- 根本原因 :
- HashMap的线程不安全:HashSet依赖的HashMap在多线程下可能因哈希冲突或扩容导致死循环或数据丢失。
 
3. Map(如HashMap)
- 问题表现 :多线程并发插入或扩容时,可能造成死循环(JDK 1.7)或数据覆盖(JDK 1.8+)。
- 根本原因 :
- 哈希表扩容冲突 :多线程同时触发扩容(resize)时,链表或红黑树重构过程中节点引用混乱。
- 非原子性put操作:多线程同时插入同一哈希桶可能导致数据覆盖。
 
- 哈希表扩容冲突 :多线程同时触发扩容(
二、解决方案
1. List的线程安全实现
- 
Collections.synchronizedList 通过同步方法包装普通List,保证原子性但性能较低: javaList<String> list = Collections.synchronizedList(new ArrayList<>());
- 
CopyOnWriteArrayList (推荐) 写操作复制新数组,读操作无锁,适合读多写少场景: javaList<String> list = new CopyOnWriteArrayList<>();
2. Set的线程安全实现
- 
Collections.synchronizedSet 同步方法包装普通Set: javaSet<String> set = Collections.synchronizedSet(new HashSet<>());
- 
CopyOnWriteArraySet (推荐) 基于 CopyOnWriteArrayList实现,适合元素少、读多写少:javaSet<String> set = new CopyOnWriteArraySet<>();
3. Map的线程安全实现
- 
ConcurrentHashMap(推荐) - JDK 1.7:分段锁(Segment),降低锁粒度。
- JDK 1.8+:CAS + synchronized锁单个哈希桶,并发性能更高。
 javaMap<String, String> map = new ConcurrentHashMap<>();
- 
Collections.synchronizedMap 同步方法包装普通Map,适用于低并发: javaMap<String, String> map = Collections.synchronizedMap(new HashMap<>());
三、对比与选型
| 集合类型 | 非安全类 | 安全实现方案 | 适用场景 | 
|---|---|---|---|
| List | ArrayList | CopyOnWriteArrayList | 读多写少(如配置信息) | 
| Collections.synchronizedList | 简单同步需求 | ||
| Set | HashSet | CopyOnWriteArraySet | 元素少、读多写少 | 
| Collections.synchronizedSet | 低并发场景 | ||
| Map | HashMap | ConcurrentHashMap | 高并发读写(首选) | 
| Collections.synchronizedMap | 兼容旧代码或低并发需求 | 
四、注意事项
- 性能权衡 :
- CopyOnWriteArrayList每次写操作复制数组,写频繁时性能差。
- ConcurrentHashMap在JDK 1.8+中优化为CAS+锁,性能接近无锁。
 
- 避免误区 :
- Vector和- Hashtable通过全表锁实现安全,但高并发下性能差,不推荐使用。
 
- 开发建议 :
- 默认使用ConcurrentHashMap替代HashMap,CopyOnWriteArrayList替代ArrayList。
- 若需强一致性(如金融场景),需结合显式锁(如ReentrantLock)或数据库事务。
 
- 默认使用
通过选择适合的并发容器,可避免ConcurrentModificationException、数据丢失等问题,同时平衡性能与线程安全。