ConcurrentHashMap 不能用 put 替代 computeIfAbsent因 put 初始化的原子性不能保证但原子性不能保证 computeIfAbsent 通过 RESERVED 状态、CAS 并保证分段锁 key 对应 value 只创建一次。ConcurrentHashMap 为什么不能直接使用put替代computeIfAbsent当多个线程同时尝试初始化某个线程 key 对应的 value(如缓存构建、单例对象创建)直接使用put写入将被覆盖或丢失computeIfAbsent能保证原子只初始化一次整个过程都是原子。它的底部被使用了Node的RESERVED状态和 CAS synchronized 分段加锁机制不是简单的“先查后查” put”。这样写常见错误if (!map.containsKey(key)) { map.put(key, expensiveCreate()); }这将在并发下多次执行expensiveCreate()部分结果可能会丢弃。正确的方法是:map.computeIfAbsent(key, k - expensiveCreate());computeIfAbsent返回是最终值无论是否新创建如果 key 已存在(即使 value 是null不会触发 mappingFunctionmappingFunction 内部不依赖外部可变状态否则可能会导致不可预测的竞争状态ConcurrentHashMap 的size()和isEmpty()为什么不准这两种方法返回的是近似值不加全局锁只总结各种方法 segment / bin 统计快照。高并发写入时调用瞬间可能会错过正在提交的修改size()可能比实际少isEmpty()可能返回true即使刚有线程 put 成功。典型误用场景用while (!map.isEmpty()) { ... }做轮询消费 —— 可以提前退出根据size() 0判断是否需要初始化 —— 条件竞争导致重复初始化替代方案取决于用途EnablePPA中小学绩效考核系统2.0无论从什么情况出发在当前校长责任制的制度安排下中小学校长作为学校的领导者、经理和教育工作者其管理水平对学校发展的重要性是不言而喻的。从这个角度来看建立科学的校长绩效评价体系和相应的评价手段和工具有利于教育行政机关对校长管理实践的整个过程及其结果、价值判断和评价有利于加强学校教学管理提高教学质量衍生校长改变管理理念提高其综合管理质量。判断是否为空改用map.containsKey(someKey)或者更清晰的哨兵标志获得精确尺寸仅在低并发或调试时使用生产逻辑不应依赖于它来控制流量需要强一致性计数搭配LongAdder单独维护ConcurrentHashMap 在 JDK 8 和 JDK 9 扩容行为的差异JDK 8 其他线程在迁移过程中发现了“多线程协助扩容” table 扩容时会主动帮助移动数据JDK 9(确切地说是 Java 11 起引入了ForwardingNode优化但关键变化是扩容阈值计算方法发生了变化对TreeBin(红黑树节点)迁移逻辑更加严格。最直接的影响是从 JDK 8 升级到 11 之后如果初始容量和负载因子手动设置则不考虑树化阈值UNTREEIFY_THRESHOLD 6更多的树化/退化可能会意外触发影响遍历性能putAll()在大集合场景中JDK 11 更倾向于批量分段扩容但如果原来 map 已接近阈值可能导致连续多次扩容使用new ConcurrentHashMap(initialCapacity)时JDK 8 实际分配是 initialCapacity 的最小 2 的幂JDK 11 还是这样但是内部sizeCtl初始化逻辑略有调整在极端情况下首次调整 put 可能多一次 resize建议在线服务升级 JDK 后观察 GC 日志中的ConcurrentHashMap特别注意相关扩容频率transferIndex和baseCount的波动。用ConcurrentHashMap实现简单线程安全计数器的陷阱很多人用map.compute(key, (k, v) - (v null ? 1 : v 1))计数似乎很简单但每次调用都会触发哈希搜索 CAS 在高频更新下竞争激烈性能远不如LongAdder。真正适合用ConcurrentHashMap计数场景如下key 空间稀疏更新频率低需要按 key 聚合和后续的具体经验或查询 key。高频单 key 计数(例如请求总数)→ 用LongAdder或AtomicLong多 key 分桶计数(如按用户计数) ID 统计→ConcurrentHashMap比嵌套compute更高效原子需要增减并返回旧值 →map.merge(key, 1L, Long::sum)比compute更清晰的语义而且 merge 底层对数字类型进行了少量优化别忽略LongAdder内存费用:其本质是数组cell每个线程都有不同的争用 cell但总内存占单个比例AtomicLong高几倍。权衡点总是吞吐。 vs 内存。