普通块缓存适合轻量级单机临时场景,基于ConcurrentHashMap实现快读写与自动同步;通过定时任务+版本比对异步拉取更新,采用双缓存切换与volatile+CAS控制同步开关,确保读路径零阻塞。

普通块缓存(即基于内存的简易键值缓存)适合轻量级、单机、非持久、临时性场景,比如配置预热、会话中间态、接口限流计数等。它不依赖外部服务(如 Redis),也不涉及复杂一致性协议,关键在于“快读写 + 自动同步”——这里的“同步”不是跨节点同步,而是指缓存与源头数据(如数据库、配置文件、远程API)之间,在可控时机下保持逻辑一致。
用线程安全的内存容器做缓存底座
Java 中推荐使用 ConcurrentHashMap 作为底层存储:它支持高并发读写,无需额外加锁,get/put 基本操作接近 O(1)。避免用 HashMap(非线程安全)或 Hashtable(全表锁,性能差)。若需过期控制,可封装一层带时间戳的 Entry,或配合 ScheduledExecutorService 定时清理。
- 示例结构:
ConcurrentHashMap,其中CacheEntry包含 value、expireTime、version(可选) - 读操作直接 get 并校验是否过期;写操作 put 新 entry 或更新旧 entry 的 value 和时间戳
- 不建议在 get 时自动刷新——这会阻塞调用线程;更适合“异步后台拉取 + 原子替换”
用定时任务触发自动同步
所谓“自动同步”,本质是定期从源头拉取最新数据,并覆盖缓存中对应 key。可用 Timer + TimerTask 或更现代的 ScheduledExecutorService 实现。重点不是高频刷新,而是“按需节奏 + 失败重试 + 版本比对”。
- 例如:每 30 秒检查一次配置中心的 version 号,仅当 version 升级才全量 reload 缓存
- 同步任务应捕获异常,记录日志,失败后指数退避重试(如 1s → 2s → 4s)
- 避免同步期间阻塞读写:采用“双缓存切换”策略——新数据加载到副本 map,加载成功后原子替换引用(
AtomicReference)
用 volatile + CAS 控制同步开关与可见性
当需要手动触发一次同步(比如运维推送信号),可用 volatile boolean 标记状态,配合 compareAndSet 防止重复提交。volatile 保证所有线程看到同一开关状态,避免因 CPU 缓存不一致导致部分线程漏执行。
- 定义:
private volatile boolean syncRequested = false; - 触发端:
if (syncRequested.compareAndSet(false, true)) { submitSyncTask(); } - 同步任务末尾重置:
syncRequested.set(false); - 注意:volatile 不保证复合操作原子性,所以不能用
syncRequested = true直接赋值来代替 CAS
读写路径要分离,避免同步阻塞业务
真实业务中,99% 是读,1% 是写或同步。必须确保读路径零阻塞、无锁、不等待网络或磁盘 I/O。所有耗时操作(HTTP 请求、DB 查询、反序列化)必须在同步线程中完成,主业务线程只做内存级 get/put。
- 读方法签名应为:
String getValue(String key),内部不 throw checked exception,失败返回 null 或默认值 - 写方法分两类:一是本地更新(
putLocal(key, val)),二是标记待同步(markDirty(key)),后者由后台线程统一处理 - 可加简单统计:用 AtomicLong 记录 hit/miss 比例,便于后续判断是否需调整过期时间或同步频率










