You need to enable JavaScript to run this app. Java基于ConcurrentHashMap实现缓存时如何处理锁应对未命中并发场景
优惠活动
大模型
产品
解决方案
定价
更多

Java基于ConcurrentHashMap实现缓存时如何处理锁应对未命中并发场景

基于ConcurrentHashMap的缓存防击穿实现方案

核心问题本质

你遇到的是典型的缓存击穿场景:基于ConcurrentHashMap的缓存虽然保证了读写操作的线程安全,但当同一个key同时出现并发未命中时,所有未命中的线程都会穿透到数据库,完全没有起到缓存的削峰作用。

适配现有结构的实现思路

不要直接给缓存加全局锁,那样会把所有key的并发操作全部串行化,完全丧失ConcurrentHashMap的并发性能优势。正确的做法是为每个待加载的key分配独立的、可等待的资源,保证同key的并发请求只触发一次数据库查询。

方案1:基于专属对象锁的轻量实现

这个方案和你现有get/put的方法结构兼容度最高,改造量最小:

  • 除了存实际业务KV的主缓存(你现有的ConcurrentHashMap实例),额外维护一个仅用于锁分配的ConcurrentHashMap:lockMap,存储结构为<缓存key, 互斥锁对象>
  • 改造get方法的执行流程:
    • 快路径:直接查询主缓存,如果命中直接返回值,无任何额外锁开销
    • 慢路径(缓存未命中):
      • 调用lockMap.computeIfAbsent(key, k -> new Object())获取当前key对应的专属锁对象。这个操作是线程安全的,同一个key在并发场景下只会生成一次锁实例,不会重复创建
      • 对拿到的锁对象加synchronized进入临界区
      • 必须执行双重检查:进入临界区后立刻再查一次主缓存。如果此时缓存已经被之前拿到锁的线程写入,直接返回缓存值,完全不需要查询数据库
      • 如果双重检查后仍未命中:当前线程是这个key的唯一数据库查询责任线程,正常执行数据库查询,拿到结果后调用你现有的put方法写入主缓存,写完后将当前key对应的锁从lockMap中移除,避免lockMap内存堆积
      • 临界区执行完成后自动释放锁,后续在等锁的线程会依次进入临界区,执行上述双重检查逻辑,直接读取已经写好的缓存值返回
  • 关键踩坑提醒:
    • 绝对不要把数据库查询、缓存写入的重逻辑放在lockMap.computeIfAbsent的lambda表达式里:ConcurrentHashMap的computeIfAbsent执行映射函数时会锁住对应数据段的桶,慢操作会直接阻塞其他无关联key的并发操作,造成性能雪崩
    • 做好异常兜底:如果负责查数据库的线程抛出异常,一定要在finally块中把对应key的锁从lockMap中清除、释放锁,不然所有等这个key的线程会永久阻塞
    • 锁的清理时机不要太早:要等缓存写完、双重检查逻辑全部完成后再清理锁,不然会出现锁被删了之后,后续进来的线程又创建新锁、重复查数据库的问题

方案2:基于CompletableFuture的无锁化优化方案

如果对性能要求更高,可以把专属锁替换成CompletableFuture,减少线程等锁的上下文切换开销:

  • 同样维护两个ConcurrentHashMap:主缓存、futureMap(存储<缓存key, CompletableFuture<缓存值>>)
  • 改造get方法的流程:
    • 快路径:主缓存命中直接返回
    • 慢路径(缓存未命中):
      • 调用futureMap.computeIfAbsent(key, k -> CompletableFuture.supplyAsync(() -> { // 仅执行一次的数据库查询、写主缓存逻辑 查数据库 -> 调用现有put方法写主缓存 -> 返回查询到的值 }))
      • 所有并发请求同一个key的线程,拿到的都是同一个CompletableFuture实例,直接调用future.join()等待结果返回即可
      • 等CompletableFuture执行完成(不管成功还是失败),在回调里把对应key从futureMap中移除,避免内存泄漏

原有put方法的适配

不需要修改你现有的put方法的核心逻辑,只需要保证在上述缓存加载流程中,写入缓存时直接复用现有put的逻辑,和其他外部触发的缓存写入逻辑保持线程安全即可。

内容的提问来源于stack exchange,提问作者user15439867

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.22 08:06:08