concurrentreferencehashmap专为带引用语义的并发缓存设计,支持弱/软引用键值,通过segment分段锁与referencequeue协同实现懒式清理,不保证实时可见性且不支持fail-fast迭代器。

ConcurrentReferenceHashMap 的核心定位
它不是 ConcurrentHashMap 的简单增强版,而是专为带引用语义的并发缓存设计的数据结构。关键差异在于:ConcurrentHashMap 保证键值的强可达性,而 ConcurrentReferenceHashMap 允许你指定键或值使用弱引用(WeakReference)或软引用(SoftReference),从而把对象生命周期部分交由 GC 管理。
引用类型如何参与数据存储
构造时通过 ReferenceType 参数决定键与值的引用强度,默认是 SOFT(软引用)。常见组合有:
- Weak keys + Strong values:最接近 WeakHashMap 的行为,适合监听类缓存(如事件监听器注册表),键一旦无外部强引用,条目即不可见
- Soft keys + Soft values:典型内存敏感型缓存,JVM 在内存压力下才回收,适合大对象缓存(如模板、解析结果)
- Strong keys + Weak values:键长期存在,值可被快速释放,适用于临时计算结果映射
注意:若键使用弱/软引用,则 null 键不被允许;若值使用弱/软引用,则 null 值不被允许。
分段锁 + 引用队列的协同机制
底层采用类似 JDK 1.7 ConcurrentHashMap 的 Segment 分段锁结构,每个 Segment 是一个独立的 ReentrantLock 保护区域。但关键改进在于每个 Segment 内部维护自己的 ReferenceManager 和关联的 ReferenceQueue。
引用清理不是实时发生的,而是“懒式触发”:
- GC 回收键或值后,对应 Reference 实例入队到该 Segment 的 ReferenceQueue
- 只有在执行 mutator 操作(如 put、remove、clear)时,才会调用 expungeStaleEntries() 扫描并清理已失效条目
- 读操作(get、containsKey)不主动清理,因此 size() 或 isEmpty() 可能返回过期数值
实际使用中的关键注意事项
它不是“开箱即用”的通用 Map 替代品,需明确其行为边界:
- 不支持 fail-fast 迭代器:遍历时即使其他线程修改了结构,迭代器仍尽力继续,不会抛 ConcurrentModificationException
- GC 行为影响可见性:同一时刻 get(key) 可能返回值,几毫秒后再次 get 就返回 null,这是正常现象,不是 bug
- 避免 key-value 循环引用:若 value 强引用 key,会阻止 key 被回收,导致内存泄漏
- 高并发写场景下,Segment 数量(concurrencyLevel)需合理设置,避免锁争用或内存浪费
当不确定是否需要引用语义时,应优先选用 ConcurrentHashMap —— 它更稳定、语义清晰、调试友好。










