zoukankan      html  css  js  c++  java
  • Guava Cache详解

    适用性

      缓存在很多场景下都是相当有用的。例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存

      Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存

      通常来说,Guava Cache适用于:

      • 你愿意消耗一些内存空间来提升速度。
      • 你预料到某些键会被查询一次以上。
      • 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Redis这类工具)

      如果你的场景符合上述的每一条,Guava Cache就适合你。

      如果你不需要Cache中的特性,使用ConcurrentHashMap有更好的内存效率——但Cache的大多数特性都很难基于旧有的ConcurrentMap复制,甚至根本不可能做到

    代码详解

      1 /**
      2  * @author LiuHuan
      3  * @date 2020-06-17 15:52
      4  * @desc Guava Cache学习
      5  */
      6 public class GuavaCacheTest {
      7 
      8     public static void main(String[] args) throws ExecutionException {
      9         GuavaCacheTest test = new GuavaCacheTest();
     10         Cache<String, String> cache = test.getGuavaCache();
     11         // 放入/覆盖一个缓存
     12         cache.put("key", "value");
     13         // 获取一个缓存,如果该缓存不存在则返回一个null值
     14         cache.getIfPresent("");
     15         // 获取缓存,当缓存不存在时,则通Callable进行加载并返回。该操作是原子
     16         cache.get("key", () -> loadingValue("key"));
     17         // 回收key为k1的缓存
     18         cache.invalidate("key");
     19         // 使用Map的put方法进行覆盖刷新
     20         cache.asMap().put("key", "value");
     21         // 使用ConcurrentMap的replace方法进行覆盖刷新
     22         cache.asMap().replace("key", "value1");
     23         // 使用Map的putAll方法进行批量覆盖刷新
     24         Map<String,String> needRefresh = ImmutableMap.of("key1","value1", "key2", "value2");
     25         cache.asMap().putAll(needRefresh);
     26         // 批量回收key为key1、key2的缓存
     27         List<String> needInvalidateKeys = Arrays.asList("key1", "key2");
     28         cache.invalidateAll(needInvalidateKeys);
     29         // 回收所有缓存
     30         cache.invalidateAll();
     31 
     32         // 用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回CacheStats对象
     33         CacheStats stats = cache.stats();
     34         // 缓存命中率
     35         double hitRate = stats.hitRate();
     36         // 加载新值的平均时间,单位为纳秒
     37         double averageLoadPenalty = stats.averageLoadPenalty();
     38         // 缓存项被回收的总数,不包括显式清除
     39         long evictionCount = stats.evictionCount();
     40 
     41         LoadingCache<String, String> loadingCache = test.getGuavaLoadingCache();
     42         // loadingCache 在进行刷新时无需显式的传入value
     43         loadingCache.refresh("key");
     44     }
     45 
     46     /**
     47      * 获取GuavaCache实例
     48      * @return
     49      */
     50     public Cache<String, String> getGuavaCache(){
     51         // 异步触发监听器
     52         RemovalListener<Object, Object> removalListener = RemovalListeners.asynchronous(removal -> {
     53             // 如果被显示移除这里为true
     54             boolean wasEvicted = removal.wasEvicted();
     55             // 移除的原因
     56             RemovalCause cause = removal.getCause();
     57         }, Executors.newSingleThreadExecutor());
     58         
     59         // 通过CacheBuilder构建一个缓存实例
     60         Cache<String, String> cache = CacheBuilder.newBuilder()
     61             // 由于Guava的缓存使用了分离锁的机制,扩容的代价非常昂贵,所以合理的初识容量能够减少扩容次数
     62             .initialCapacity(100)
     63             // 设置缓存的最大条数
     64             .maximumSize(100)
     65             // maximumWeight逻辑上用来表示一种“权重”,这里与maximumSize冲突,设置一个即可
     66             // 这里我们将key和value所占的字节数,作为weight,当cache中所有的“weight”总和达到maximumWeight时,将会触发“剔除策略”
     67             .maximumWeight(1024 * 1024)
     68             .weigher((Weigher<String, String>)(key, value) -> key.getBytes().length + value.getBytes().length)
     69             // 使用弱引用存储键。当键没有其它(强或软)引用时,该缓存可能会被回收
     70             .weakKeys()
     71             // 使用弱引用存储值。当值没有其它(强或软)引用时,该缓存可能会被回收
     72             .weakValues()
     73             // 使用软引用存储值。当内存不足并且该值其它强引用引用时,该缓存就会被回收
     74             // 通过软/弱引用的回收方式,相当于将缓存回收任务交给了GC,使得缓存的命中率变得十分的不稳定,在非必要的情况下,还是推荐基于数量和容量的回收
     75             .softValues()
     76             // 设置缓存在写入一分钟后失效
     77             .expireAfterWrite(1, TimeUnit.MINUTES)
     78             // 设置缓存最后一次访问10分钟之后失效,与session类似。与expireAfterWrite冲突,设置一个即可
     79             .expireAfterAccess(Duration.ofMinutes(10))
     80             // 开启缓存统计
     81             .recordStats()
     82             // 设置并发级别为CPU核心数
     83             .concurrencyLevel(Runtime.getRuntime().availableProcessors())
     84             // 移除监听器,这里是移除缓存时同步调用,会拖慢正常的请求
     85             .removalListener(removal -> {
     86                 // 如果被显示移除这里为true
     87                 boolean wasEvicted = removal.wasEvicted();
     88                 // 移除的原因
     89                 RemovalCause cause = removal.getCause();
     90             })
     91             // 异步触发监听器
     92             .removalListener(removalListener)
     93             .build();
     94         return cache;
     95     }
     96 
     97     /**
     98      * 获取GuavaLoadingCache实例,会有默认的缓存加载策略
     99      * @return
    100      */
    101     public LoadingCache<String, String> getGuavaLoadingCache(){
    102         // 通过CacheBuilder构建一个缓存实例
    103         LoadingCache<String, String> cache = CacheBuilder.newBuilder()
    104             // 设置缓存在写入10分钟后,通过CacheLoader的load方法进行刷新
    105             .refreshAfterWrite(Duration.ofMinutes(10))
    106             .build(new CacheLoader<String, String>() {
    107                 @Override
    108                 public String load(String key) throws Exception {
    109                     // 缓存加载策略
    110                     return loadingValue(key);
    111                 }
    112 
    113             });
    114         return cache;
    115     }
    116 
    117     /**
    118      * 缓存加载策略
    119      * @param key
    120      * @return
    121      */
    122     private static String loadingValue(String key){
    123         return null;
    124     };
    125 
    126 }

    缓存清理  

      使用CacheBuilder构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。相反,它会在写操作时顺带做少量的维护工作,如果写操作实在太少的话,偶尔在读操作时做这个操作。这样做的原因在于:如果要自动地持续清理缓存,就必须有一个线程,这个线程会和用户操作竞争共享锁。此外,某些环境下线程创建可能受限制,这样CacheBuilder就不可用了。

      如果你的缓存是高吞吐的,那就无需担心缓存的维护和清理等工作。如果你的缓存只会偶尔有写操作,而你又不想清理工作阻碍了读操作,那么可以创建自己的维护线程,以固定的时间间隔调用Cache.cleanUp()ScheduledExecutorService可以帮助你很好地实现这样的定时调度

  • 相关阅读:
    SQL SERVER 2012修改数据库名称(包括 db.mdf 名称的修改)
    vmware三种网络模式
    指针
    linux 中 开放端口,以及防火墙的相关命令
    数据库备份的脚本,记录下,还需优化下
    遍历 目录的几种有效办法
    转。git 乌龟的使用安装
    centos 时区正确,时间不对
    locate
    从 零开始 无差错 装好nginx+PHP
  • 原文地址:https://www.cnblogs.com/ding-dang/p/13153788.html
Copyright © 2011-2022 走看看