Java多线程使用ConcurrentHashMap统计词频结果不一致问题求解
多线程统计结果不一致问题分析
核心根因:竞态条件
ConcurrentHashMap仅能保证单个方法调用的原子性,无法保证多个方法组合操作的原子性,你最初的代码应该是使用了containsKey+put的if/else逻辑:
// 你最初的错误逻辑(未在当前贴出的代码中展示) if (map.containsKey(word)) { map.put(word, map.get(word) + 1); } else { map.put(word, 1); }
这段逻辑包含了「查询key是否存在」「读取旧值」「写入新值」三个独立的原子操作,三个操作整体没有原子性保证:当两个线程同时处理同一个单词时,可能出现线程A读取到旧值后还没写入新值,线程B也读取到了相同的旧值,最终两个线程的写入互相覆盖,导致统计次数比实际值小。
为什么第一次替换为merge方法仍有问题
你编辑1中提到替换merge后问题仍然存在,是因为你没有完全删除if/else的存在性判断逻辑,仅把put操作替换成了merge:
// 错误修改示例 if (map.containsKey(word)) { map.merge(word, 1, Integer::sum); } else { map.put(word, 1); }
这种写法仍然存在containsKey和后续写入操作的组合竞态条件,依然会出现统计错误。
为什么删除if/else仅保留merge就正常
ConcurrentHashMap的merge方法本身是原子操作,它的「判断key是否存在」「读取旧值」「计算新值」「写入新值」整个流程是加锁保证原子性的,不会出现多线程写入覆盖的问题,同时天然支持key不存在时直接写入初始值1的逻辑,不需要额外做存在性判断,所以单独使用merge就能得到正确的统计结果。
补充说明
你当前贴出的代码已经是完全正确的写法,实际运行会稳定输出a:8、b:6的结果,不会再出现统计值不稳定的问题。
内容的提问来源于stack exchange,提问作者Thanasis Namikazee
相关产品推荐
相关产品推荐

