Caffeine Cache驱逐过程中读取行为及expireAfterWrite并发特性问询
你给出的Caffeine Cache初始化代码如下:
Cache<Long, Info> cache = newBuilder() .expireAfterWrite(getCacheMaxNbDays(), DAYS) .maximumSize(getCacheMaxSize()) .build();
针对两个问题的解答如下:
问题1:cache.asMap() 并发驱逐时的返回逻辑
Caffeine 的 asMap() 提供的是弱一致性视图,调用时不会加锁,也不会等待后台驱逐任务执行完成。返回的结果是调用瞬间所有未被标记为失效的条目,正在执行驱逐逻辑但未完成移除的条目不会出现在返回结果中,当前调用线程也不会被阻塞等待驱逐结束。
如果需要获取驱逐完成后的全量有效条目,可以先主动调用 Cache.cleanUp() 同步执行所有待处理的驱逐任务,再调用 asMap() 即可。
问题2:expireAfterWrite 阻塞逻辑与并发处理
expireAfterWrite 本身不属于阻塞操作:时间到期后的驱逐逻辑默认由后台异步线程执行,不会阻塞正常的读写请求,仅在读写操作触发驱逐阈值时,才会在用户线程同步处理有限个过期条目后立即返回,不会长时间占用用户线程。
当同一条目的读写操作和驱逐操作并发执行时,Caffeine 采用细粒度的条目级锁保证原子性,处理逻辑如下:
- 读写操作执行时会先校验条目是否过期,若已过期会加锁保证只有一个线程负责移除/重新加载该条目,其余同条目请求会等待新值加载完成后返回,不会读到过期数据
- 如果后台驱逐线程先拿到条目锁完成了失效标记,后续读写请求会直接判定条目失效,触发重新加载逻辑
- 如果读写请求先拿到条目锁,驱逐线程会跳过该条目的本次驱逐,下次调度时再重新校验有效期
内容的提问来源于stack exchange,提问作者Mamadou Bachir Barry
相关产品推荐
相关产品推荐

