大数量下ConcurrentHashMap出现重复键问题求助
问题原因及解决方案
核心原因:非原子性的“检查-插入”操作引发竞态条件
你遇到的重复问题本质是:ConcurrentHashMap的单个操作线程安全,但组合的“检查key是否存在+插入key”操作并非原子性,在50线程高并发场景下触发了竞态条件:
- 当两个线程同时处理同一个rowkey时,线程A执行
containsKey(key)返回false,线程B在A执行put(key)前也执行containsKey(key)返回false,最终两个线程都完成了put操作,导致同一个key被重复存入。 - 小数据量时线程竞争概率极低,因此不会触发问题;当数据量接近百万级、线程数较多时,竞态条件触发概率显著提升,所以稳定出现重复,且重复条目随线程调度时机变化而不同。
错误逻辑示例(你大概率采用了类似写法)
如果你的去重逻辑是如下代码,必然会出现重复:
// 错误:containsKey和put是两个独立操作,存在竞态窗口 if (!globalConcurrentMap.containsKey(rowkey)) { globalConcurrentMap.put(rowkey, "标记值"); // 写入HBase的逻辑 }
正确的原子性去重方案
使用ConcurrentHashMap提供的原子性方法putIfAbsent,它会在同一个原子操作内完成“检查key是否存在+不存在则插入”的逻辑:
// 正确:putIfAbsent是原子操作,确保同一个key只会被插入一次 if (globalConcurrentMap.putIfAbsent(rowkey, "标记值") == null) { // 仅当key之前不存在时,才执行写入HBase的逻辑 }
putIfAbsent返回值规则:
- 若key不存在,插入并返回
null,此时进入分支执行写入; - 若key已存在,直接返回已有值,跳过写入逻辑。
这种写法能彻底避免多线程下的竞态条件,保证去重结果与MySQL distinct的结果一致。
内容的提问来源于stack exchange,提问作者william
相关产品推荐
相关产品推荐

