Guava Cache基本使用

前言:

Guava 学习笔记,参考自Google Guava官方文档。

Guava Cache

Cache的回收机制

既然是缓存,那么总会存在没有足够的内存缓存所有数据。Cache提供三大类回收机制。

基于容量回收

如果要规定缓存项的数目不超过固定值,只需使用CacheBuilder.maximumSize(long)]。缓存将尝试回收最近没有使用或总体上很少使用的缓存项。——警告:在缓存项的数目达到限定值之前,缓存就可能进行回收操作——通常来说,这种情况发生在缓存项的数目逼近限定值时。

基于定时回收

CacheBuilder提供两种定时回收的方法:

  • expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
  • expireAfterWrite(long, TimeUnit):缓存项在给定时间’'内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
基于引用类型回收

通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:

  • CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用键的缓存用而不是equals比较键。
  • CacheBuilder.weakValue():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用值的缓存用而不是equals比较值。
  • CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。

具体功能使用

加载 CacheLoader

创建CacheLoader实现load(方法即可。当使用get()方法的时候,要么返回容器中已经缓存的值,要么就是使用CacheLoader想缓存原子的加载新值。、

如果部分场景需要使用getAll(),可以重载CacheLoader.loadAll()提高效率。若不重载则会单独调用CacheLoader.load()进行加载。

LoadingCache<Key, Object> graphs = CacheBuilder.newBuilder()
        .build(
    		//
            new CacheLoader<Key, Object>() {
                public Graph load(Key key) throws AnyException {
                    return getObjectFromDb(key);
                }
       });
Callable

方便自定义每次的获取逻辑, 原理跟CacheLoader.load()一致。 如果缓存中不存在,则调用callable()来进行获取放入缓存中。

Cache<Key, Graph> cache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .build(); // look Ma, no CacheLoader
...
try {
    // If the key wasn't in the "easy to compute" group, we need to
    // do things the hard way.
    cache.get(key, new Callable<Key, Graph>() {
        @Override
        public Value call() throws AnyException {
            return doThingsTheHardWay(key);
        }
    });
} catch (ExecutionException e) {
    throw new OtherException(e.getCause());
}

显式插入

使用cache.put(key, value)方法可以直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值。使用Cache.asMap()视图提供的任何方法也能修改缓存。但请注意,asMap视图的任何方法都不能保证缓存项被原子地加载到缓存中。进一步说,asMap视图的原子运算在Guava Cache的原子加载范畴之外,所以相比于Cache.asMap().putIfAbsent(K, V),Cache.get(K, Callable<V>)应该总是优先使用。 即更多的依赖自动加载而不是手动的进行添加。

元素移除监听器 removalListener

通过CacheBuilder.removalListener(RemovalListener)你可以声明一个监听器,以便缓存项被移除时做一些额外操作。

asMap视图

asMap视图提供了缓存的ConcurrentMap形式,但asMap视图与缓存的交互需要注意:

  • cache.asMap()包含当前所有加载到缓存的项。因此相应地,cache.asMap().keySet()包含当前所有已加载键;
  • asMap().get(key)实质上等同于cache.getIfPresent(key),而且不会引起缓存项的加载。这和Map的语义约定一致。
  • 所有读写操作都会重置相关缓存项的访问时间,包括Cache.asMap().get(Object)方法和Cache.asMap().put(K, V)方法,但不包括Cache.asMap().containsKey(Object)方法,也不包括在Cache.asMap()的集合视图上的操作。比如,遍历Cache.asMap().entrySet()不会重置缓存项的读取时间。

测试代码

package com.justloseit.common.test.guavaTest.guavaCache;

import com.google.common.base.Optional;
import com.google.common.cache.*;
import com.google.common.collect.ImmutableMap;


import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Classname CacheTest
 * @Description guava缓存 Test
 * @Author lqhao
 * @Date 2021/8/16 15:48
 * @Version 1.0
 */
public class CacheTest {
    /**
     * 移除监听器
     */
    static RemovalListener<Integer, Student> MY_LISTENER_01 = removal -> {
        Student student = removal.getValue();
        System.out.println(student.getName()+"被移除了");
    };
    /**
     * 异步移除监听器  需要添加异步执行的线程池
     */
    static RemovalListener<Integer, Student> MY_LISTENER_02=RemovalListeners.asynchronous(removal -> {
        Student student = removal.getValue();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(student.getName()+"被异步移除了");
    }, Executors.newSingleThreadExecutor());

    /**
     * Guava缓存
     */
    static LoadingCache<Integer, Student> studentCaches = CacheBuilder.newBuilder().softValues()
            .maximumSize(1)// 固定容器的大小
            .expireAfterWrite(10, TimeUnit.MINUTES)//缓存项在给定时间内没有被写访问(创建或覆盖),则回收
//            .removalListener(MY_LISTENER_01) //同步
            .removalListener(MY_LISTENER_02)   //异步
            .build(
                    //缓存中不存在则主动的进行加载
                    new CacheLoader<Integer, Student>() {
                        @Override
                        public Student load(Integer key) {
                            Optional<Student> optional=Student.get(key);
                            if (optional.isPresent()){
                                return optional.get();
                            }else{
                                return Student.valueOf(key, 18, "awake");
                            }
                        }
                    });

    /**
     * 主函数
     * @param args
     * @throws ExecutionException
     */
    public static void main(String[] args) throws ExecutionException {
        System.out.println(studentCaches.get(1));
        System.out.println(studentCaches.get(0));
        System.out.println(studentCaches.size());

    }


    static class Student{
        private int id;
        private int age;
        private String name;
        private static ImmutableMap<Integer, Student> studentImmutableMap=ImmutableMap.of(
                1, Student.valueOf(1, 18, "lqhao"),
                2, Student.valueOf(2, 19, "zpwen"),
                3, Student.valueOf(3, 20, "qyyi")
        );

        public static Optional<Student> get(Integer id) {
            Student student=studentImmutableMap.get(id);
            return Optional.fromNullable(student);
        }

        public static Student valueOf(int id, int age, String name) {
            Student student=new Student();
            student.id=id;
            student.age=age;
            student.name=name;
            return student;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值