LoadingCache缓存的使用

本文详细介绍了SpringBoot中使用Guava的LoadingCache进行缓存管理的方法,包括依赖引入、规则设定如过期时间、数据加载及异步刷新等关键特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot中使用LoadingCache缓存需要在pom中引用依赖

依赖

引用的依赖:

		<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>27.1-jre</version>
        </dependency>
方法介绍

CacheBuilder:是 LoadingCache实现类,存储键值形式(本质Map)。用来定义各种规则。
最常用的方法的介绍:

  1. 定义对象个数的限制,默认的过期规则是(没有定义过期规则)是在超出限制的时候,会将最远时间最少使用的自动过期保证总数在设置的范围内
public CacheBuilder<K, V> maximumSize(long size)
  1. 定义过期时间显示(duration:设置持续时间;unit:持续时间单位)缓存中数值在指定时间没有值更新会过期。
public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) 
  1. 同样是定义过期时间,只要缓存中的数据被访问就会重置数据的缓存时间。
public CacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit)
refreshAfterWrite(long duration, TimeUnit unit)
  1. 同样是定义过期时间,只是重新加载过期数据的方法不一样,属于多线程操作。
public CacheBuilder<K, V>  refreshAfterWrite(long duration, TimeUnit unit)
使用方法:
使用 expireAfterWrite(long duration, TimeUnit unit) 和 expireAfterAccess(long duration, TimeUnit unit)方法

需要继承和实现CacheLoader 的方法== load(String name)== ,该方法用于过期数据重新加载,或者是初始化数据

 V load(K key);
  1. 可以不实现逻辑,即返回null。
  2. 可以做一些数据初始化,但是这个操作可能会被多次执行。原因:当我们调用 LoadingCacheget(String key) 时,首先会从缓存里获取数据,如果没有就会调用该方法。将该方法的返回数据。和传入的name组装成 key-value 加入缓存中。所以当我们多次调用get(String key).但是缓存中有没有时 load(String name) 就会被多次执行。但是同一个name只会被执行一次(默认不是返回null的情况下)。
  3. 使用了expireAfterWrites之后,每次缓存失效LoadingCache都会去调用我们实现的 load() 方法去重新加载缓存,在加载期间,所有线程对该缓存key的访问都将被block。所以如果实际加载缓存需要较长时间的话,这种方式不太适用。还需要注意的是,这个主动调用load() 方法去重新加载缓存 只是在被调用 get(String key)方法的时候。

测试代码如下:

public class LoadingCacheTest {
    /**
     * 第一种直接定义,但是要有一个实现类 该实现类 继承 CacheLoader 并实现 load(String key) 方法
     */
    private static LoadingCache<String, Map<String, Object>> listLoadingCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build(new MyCacheLoader());


    public static void main(String[] args) {
        //SpringApplication.run(UsulTestStartApplication.class, args);

        Runnable runnable1 = () -> {
            try {
                Map<String, Object> map = new HashMap<>();
                map.put("333333","3143134");
                listLoadingCache.put("333333",map);
                System.out.println("key:333333------value:" + map.get("333333"));

                map = listLoadingCache.get("1111111");
                System.out.println("key:fafda------value:" + map.get("fafda"));

                map = listLoadingCache.get("1111111");
                System.out.println("key:fafda------value:" + map.get("fafda"));

                map = listLoadingCache.get("22222222");
                System.out.println("key:fafda------value:" + map.get("fafda"));

            } catch (ExecutionException e) {
                e.printStackTrace();
            }

            try {
                Thread.currentThread().sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            for(Map.Entry<String, Map<String, Object>> entry : listLoadingCache.asMap().entrySet()){
                System.out.println(entry.getKey() + "---第一次---" + "key:fafda------value:" + entry.getValue().get("fafda"));
            }

            Map<String, Object> map = null;
            try {
                map = listLoadingCache.get("22222222");
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.println("key:fafda------value:" + map.get("fafda"));

        };
        Thread thread1 = new Thread(runnable1);
        thread1.start();


    }


    private static class MyCacheLoader extends CacheLoader<String,Map<String, Object>> {
        @Override
        public Map<String, Object> load(String name) throws Exception {
            System.out.println("数据初始化:" + name);
            Map<String, Object> map = new HashMap<>();
            map.put("fafda","dfadada");
            return map;
        }
    }
}

expireAfterAccess(long duration, TimeUnit unit) 方法就不在介绍了,只要将睡眠时间条短一点就能册数出来。

使用 refreshAfterWrite(long duration, TimeUnit unit)方法

使用方法时需要同时实现 CacheLoader 的 两个方法:

public V load(K key);    //该方法用于初始化数据,或者第一次获取数据时缓存中没有就会调用该方法。
public ListenableFuture<V> reload(K key, V oldValue); //过期数据从新加载方式,异步实现

在使用refreshAfterWrites之后,需要自己实现CacheLoader的reload方法,在方法中创建一个ListenableFutureTask,然后将这个task提交给线程池去异步执行,reload方法最后返回这个ListenableFutureTask。这样做使缓存失效后重新加载就变成了异步,加载期间尝试获取缓存的线程也都不会被block,而是获取到加载之前的值。当加载完毕之后,各个线程就能取到最新值了。

测试方法:

public class LoadingCacheTest {
  
    private static ExecutorService executorService = Executors.newFixedThreadPool(8);
    private static LoadingCache<String, Map<String, Object>> loadingCache = CacheBuilder.newBuilder().refreshAfterWrite(5, TimeUnit.SECONDS).build(new MyCacheLoader());

    public static void main(String[] args) {
        Runnable runnable2 = () -> {
            try {
                Map<String, Object> map = new HashMap<>();
                map.put("333333","3143134");
                loadingCache.put("333333",map);
                System.out.println("key:333333------value:" + map.get("333333"));

                map = loadingCache.get("1111111");
                System.out.println("key:fafda------value:" + map.get("fafda"));

                map = loadingCache.get("1111111");
                System.out.println("key:fafda------value:" + map.get("fafda"));

                map = loadingCache.get("22222222");
                System.out.println("key:fafda------value:" + map.get("fafda"));

            } catch (ExecutionException e) {
                e.printStackTrace();
            }

            try {
                Thread.currentThread().sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Map<String, Object> map = null;
            try {
                map = loadingCache.get("22222222");
                System.out.println("失效重新加载中:key:fafda------value:" + map.get("fafda"));

                Thread.currentThread().sleep(20000);
                map = loadingCache.get("22222222");
                System.out.println("失效后已经加载完成:key:fafda------value:" + map.get("fafda"));

            } catch (Exception e) {
                e.printStackTrace();
            }

        };
        Thread thread1 = new Thread(runnable2);
        thread1.start();
    }


    private static class MyCacheLoader extends CacheLoader<String,Map<String, Object>> {
        @Override
        public Map<String, Object> load(String name) throws Exception {
            System.out.println("load数据初始化:" + name);
            Map<String, Object> map = new HashMap<>();
            map.put("fafda","dfadada");
            return map;
        }

        @Override
        public ListenableFuture<Map<String, Object>> reload(String key, Map<String, Object> oldValue) throws Exception{
            System.out.println("reload数据初始化:" + key + "----" +oldValue);
            ListenableFutureTask<Map<String, Object>> futureTask = ListenableFutureTask.create(() -> {
                Map<String, Object> map = new HashMap<>();
                Thread.currentThread().sleep(10000);
                map.put("fafda","111111111");
                return map;
            });
            executorService.execute(futureTask);
            return futureTask;
        }
    }

}

测试结果:

key:333333------value:3143134
load数据初始化:1111111
key:fafda------value:dfadada
key:fafda------value:dfadada
load数据初始化:22222222
key:fafda------value:dfadada
reload数据初始化:22222222----{fafda=dfadada}
失效重新加载中:key:fafda------value:dfadada
reload数据初始化:22222222----{fafda=111111111}
失效后已经加载完成:key:fafda------value:111111111

由测试结果可以知道。当数据失效时,使用refreshAfterWrite()方法获取到的是旧的值。缓存的刷新是异步的。刷新之后就能获取到最新的值。

以上就是 LoadingCache缓存的使用介绍。有任何错误之处欢迎指正,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值