你在guava cache上设置的更新参数是否有用?

guava cache是一种支持自动回收、刷新的concurrentHashMap。显然防止内存溢出是其核心功能,包括主动刷新refresh、过期失效expire、java的软/虚引用以及设置最大的size和weight。在实际开发中,个人对于expire和refresh使用较多,expire又分为expireAfterAccess、expireAfterWrite,现对比下三者的区别:

expireAfterAccess expireAfterWrite refreshAfterWrite
功能 读写后回收 写后回收 定时刷新
更新方法 load load 首次load,其他reload
触发线程 读取线程 读取线程 读取线程+异步线程(如果重写reload使用额外线程,默认没异步)
并行非更新线程同步 通过ReentrantLock加锁,其他线程等待加载完成 通过ReentrantLock加锁 ,其他线程等待加载完成 不加锁,其他线程返回旧值
高并发读取问题 可能造成同一key永不过期 - -
高并发更新问题 频繁上锁解锁导致性能问题 频繁上锁解锁导致性能问题 -
低频读取问题 - - refresh并非异步线程定时刷新,而是由请求线程触发,在低频访问下,某个key的value可能是较早之前读取留下的,距离现在已久,会读取到较老的值

综上所看,expire和refresh都各有优缺点,refresh在低频访问可能获取到一个较老的值,而expire在高频率的收回时候因为每个线程都会加锁解锁而有性能问题。实际开发中,将两者综合使用,通过各自的优点屏蔽掉对方的缺点。比如设定refresh为1分钟一次,expireAfterWrite为2分钟一次,那么在访问高峰下,大部分会通过refresh的异步线程触发更新,避免了加锁。而当程序处于访问低谷时候,通过expire设置最大有效期为2分钟回收,后进来的线程发现已被回收再通过加锁的方式读取新值,因为量小,加锁的性能问题也可以很好的规避。

源码

可能有些同学会问,如果refresh和expireAfterWrite的更新频率都设置成一样的话,比如都是1分钟更新,那究竟是通过哪一种方式来更新呢?

答案是通过expire来更新。通过下面简化版的源码分析原因。

//与concurrentHashMap一样,获取值时候传入key与计算key的hash
V get(K key,int hash )  {
   
          ReferenceEntry<K, V> e = getEntry(key, hash);
          if (e != null) {
   
            //获取当前时间
            long now = map.ticker.read();
            //获取存活的值,即判断是否有过期的值
            V value = getLiveValue(e, now);
            if (value != null) {
   
              
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值