hashset非线程安全,高效并发应选concurrenthashmap.newkeyset()、copyonwritearrayset或分治策略,避免共享修改。

HashSet 本身不是线程安全的,多线程下直接使用会导致数据错乱或异常(如 ConcurrentModificationException),不能靠它实现“高效”的并发集合运算。 真正高效的方案是避开非线程安全容器,选用专为并发设计的数据结构或策略。
用 ConcurrentHashMap 配合 keySet 模拟 Set 行为
ConcurrentHashMap 是 JDK 提供的高性能线程安全 Map,它的 keySet() 返回一个支持并发读写的 Set 视图(JDK 8+):
- 添加、删除、contains 操作都是线程安全且无锁(部分操作使用 CAS 或分段锁)
- 支持高并发读,写操作不影响读(类似读写分离)
- 不保证迭代时的强一致性,但不会抛 ConcurrentModificationException
示例:
SetconcurrentSet.add("a");
concurrentSet.parallelStream().filter(s -> s.startsWith("a")).count();
用 CopyOnWriteArraySet(适合读多写少场景)
底层基于数组,每次修改都复制整个数组。适用于:
– 迭代远多于增删
– 集合规模小(几百以内)
– 要求迭代过程绝对安全(不会抛异常、看到“某次快照”)
- add/remove 是同步的,性能随元素增多明显下降
- 迭代器不支持 remove(),也不反映后续修改
不适合高频写入或大数据量场景,但代码最简单、零配置。
用 Collections.synchronizedSet 包装(不推荐用于高性能场景)
只是给 HashSet 加了 synchronized 外壳,所有方法串行执行:
- 并发度低:哪怕两个线程操作不同元素,也会互相阻塞
- 复合操作仍需手动同步(如“检查不存在再添加”必须加外部锁)
- 迭代仍需显式同步,否则可能出错
仅适合低并发、过渡期改造或对性能无要求的场景。
按业务需求拆分并行计算逻辑
如果目标是“高效集合运算”(如并集、交集、去重统计),不要执着于共享一个 Set 实例:
- 各线程独立构建本地 HashSet,最后用 ConcurrentHashMap.merge 或流式归并
- 用 Parallel Stream + 不变集合(如 ImmutableSet)做中间聚合,避免共享状态
- 对大批量数据去重,可考虑分片 + 布隆过滤器预判 + 最终合并
例如多个线程分别处理一批 ID,最终汇总去重:
ConcurrentHashMaplistOfBatches.parallelStream()
.forEach(batch -> batch.forEach(id -> globalSet.putIfAbsent(id, true)));
// globalSet.keySet() 即最终去重结果
不复杂但容易忽略:高效 ≠ 用同一个集合对象被所有线程狂改,而在于减少竞争、合理分治、选对工具。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











