美文网首页
guava cache 简单入门

guava cache 简单入门

作者: snoweek | 来源:发表于2018-08-24 16:49 被阅读711次

    最简单的例子

    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, String> cache = CacheBuilder.newBuilder().build();
            cache.put(1, "a");
            System.out.println(cache.getIfPresent(1));
            System.out.println(cache.getIfPresent(2));
        }
    }
    
    

    执行结果

    若已缓存过,返回缓存的值,否则获取不到值。

    如果没有缓存,我也想要一个缓存值

    有两种方法可以实现"如果有缓存则返回;否则运算、缓存、然后返回"。

    CacheLoader

    Callable:所有类型的Guava Cache,不管有没有自动加载功能,都支持get(K, Callable<V>方法。

    public class GuavaCacheTest {
        public static void main(String[] args) {
            LoadingCache<Integer, String> cache = CacheBuilder.newBuilder().build(
                    new CacheLoader<Integer, String>() {
                        @Override
                        public String load(Integer key) throws Exception {
                            return "key-" + key;
                        }
                    }
            );
            cache.put(1, "a");
            System.out.println(cache.getIfPresent(1));
            try {
                System.out.println(cache.get(2));
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, String> cache = CacheBuilder.newBuilder().build();
            cache.put(1, "a");
            System.out.println(cache.getIfPresent(1));
            try {
                String value = cache.get(2, new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        return "hello,world";
                    }
                });
                System.out.println(value);
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    内存一直被占用?可怕:缓存回收

    上面的简单例子中,内存一直会被占用,这是不合理的,我们需要按照一定的规则将缓存清除,释放内存,防止发生OOM。

    guava提供了三种缓存回收的策略:基于容量回收、定时回收和基于引用回收。

    基于容量的回收:

    规定缓存项的数目不超过固定值,只需使用CacheBuilder.maximumSize(long)。缓存将尝试回收最近没有使用或总体上很少使用的缓存项。——警告:在缓存项的数目达到限定值之前,即缓存项的数目逼近限定值时缓存就可能进行回收操作。这个size指的是cache中的条目数,不是内存大小或是其他.

    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, String> cache = CacheBuilder.newBuilder().maximumSize(2).build();
            cache.put(1, "a");
            cache.put(2, "b");
            cache.put(3, "c");
            System.out.println(cache.asMap());
            System.out.println(cache.getIfPresent(2));
            cache.put(4, "d");
            System.out.println(cache.asMap());
        }
    }
    

    基于容量的缓存回收还可以指定缓存项的权重,使用CacheBuilder.weigher(Weigher)指定一个权重函数,并且用CacheBuilder.maximumWeight(long)指定最大总重。

    缓存回收也是在重量逼近限定值时就进行了,还要知道重量是在缓存创建时计算的,因此要考虑重量计算的复杂度。

    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, Integer> cache = CacheBuilder.newBuilder().maximumWeight(100)
                    .weigher(new Weigher<Integer, Integer>() {
                        @Override
                        public int weigh(Integer key, Integer value) {
                            if (value % 2 == 0) {
                                return 20;
                            } else {
                                return 5;
                            }
                        }
                    }).build();
    //         放偶数
            for (int i = 0; i <= 20; i += 2) {
                cache.put(i, i);
            }
            System.out.println(cache.asMap());
            cache.invalidateAll();
            for (int i = 1; i < 10; i += 1) {
                cache.put(i, i);
            }
            System.out.println(cache.asMap());
        }
    }
    

    CacheBuilder.maximumSize(long),CacheBuilder.maximumWeight(long)是互斥的,只能二选一。

    CacheBuilder.maximumSize(long)中如果每个条目占用内存空间都是相同的,就等价于限制了缓存空间的总大小;如果每个缓存条目大小不定,那么就没有办法限制总的内存大小。

    CacheBuilder.maximumWeight(long)可以用来控制内存。比如我们将总权重设置为1G(代表内存空间大小),而每个缓存条目的权重都是缓存值实际占用的内存空间大小

    基于时间的回收

    guava 提供两种定时回收的方法

    expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。

    expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。

    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).build();
            cache.put(1,1);
            System.out.println(cache.asMap());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(cache.asMap());
        }
    }
    
    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, Integer> cache = CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS).build();
            cache.put(1,1);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cache.getIfPresent(1);
            System.out.println(cache.asMap());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(cache.asMap());
        }
    }
    

    基于时间的缓存回收可以和基于容量的缓存回收一起使用,这样可以避免:当缓存创建速度,远远大于过期速度的时候出现OOM的问题。

    Cache<Integer, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).expireAfterAccess(3, TimeUnit.SECONDS).build();
    

    基于引用的回收

    手动清除缓存

    任何时候,你都可以显式地清除缓存项,而不是等到它被回收:

    个别清除:Cache.invalidate(key)

    批量清除:Cache.invalidateAll(keys)

    清除所有缓存项:Cache.invalidateAll()

    public class GuavaCacheTest {
        public static void main(String[] args) {
            Cache<Integer, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).expireAfterAccess(3, TimeUnit.SECONDS).build();
            cache.put(1, 1);
            cache.put(2, 2);
            cache.invalidateAll(Lists.newArrayList(1));
            System.out.println(cache.asMap());
            cache.put(3, 3);
            System.out.println(cache.asMap());
            cache.invalidateAll();
            System.out.println(cache.asMap());
        }
    }
    

    监听器

    public class GuavaCacheTest {
        public static void main(String[] args) {
            LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> notification) {
                    System.out.println("remove key[" + notification.getKey() + "],value[" + notification.getValue() + "],remove reason[" + notification.getCause() + "]");
                }
            }).recordStats().build(
                    new CacheLoader<Integer, Integer>() {
                        @Override
                        public Integer load(Integer key) throws Exception {
                            return 2;
                        }
                    }
            );
            cache.put(1, 1);
            cache.put(2, 2);
            System.out.println(cache.asMap());
            cache.invalidateAll();
            System.out.println(cache.asMap());
            cache.put(3, 3);
            try {
                System.out.println(cache.getUnchecked(3));
                Thread.sleep(4000);
                System.out.println(cache.getUnchecked(3));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    刷新

    public class GuavaCacheTest {
        public static void main(String[] args) {
            LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> notification) {
                    System.out.println("remove key[" + notification.getKey() + "],value[" + notification.getValue() + "],remove reason[" + notification.getCause() + "]");
                }
            }).recordStats().build(
                    new CacheLoader<Integer, Integer>() {
                        @Override
                        public Integer load(Integer key) throws Exception {
                            return 2;
                        }
                    }
            );
            cache.put(1, 1);
            cache.put(2, 2);
            System.out.println(cache.asMap());
            cache.refresh(1);
            System.out.println(cache.asMap());
        }
    }
    
    • 刷新表示为键加载新值,这个过程可以是异步的。在刷新操作进行时,缓存仍然可以向其他线程返回旧值.

    • 而不像回收操作,读缓存的线程必须等待新值加载完成。

    • 如果刷新过程抛出异常,缓存将保留旧值,

    统计

    CacheBuilder.recordStats()用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回对象以提供如下统计信息:
    hitRate():缓存命中率;
    averageLoadPenalty():加载新值的平均时间,单位为纳秒;
    evictionCount():缓存项被回收的总数,不包括显式清除
    统计信息对于调整缓存设置是至关重要的,在性能要求高的应用中我们建议密切关注这些数据。

    其他

    asMap()方法获得缓存数据的ConcurrentMap<K, V>快照

    cleanUp()清空缓存

    相关文章

      网友评论

          本文标题:guava cache 简单入门

          本文链接:https://www.haomeiwen.com/subject/pmciiftx.html