Caffeine本地缓存快速上手教程,通俗易懂

1. 概述

使用缓存的优点是可以减少直接访问数据库的压力。Caffeine是目前单机版缓存性能最高的,提供了最优的缓存命中率。用法和java中的map集合比较类似,底层使用一个ConcurrentHashMap来保存所有数据,可以理解为一个增强版的map集合,增强的功能有设置缓存过期时间,缓存数据驱逐,统计缓存数据等。本文会大量使用详细的代码示例,通俗易懂地帮助大家学会使用Caffeine本地缓存。

常见QA

  1. Caffeine和redis的区别?共同点都是基于内存。其中,Caffeine是本地缓存,基于单个JVM,不能直接跨多台机器分布,如果程序停止,JVM停止,本地缓存数据会全部丢失,类似java中的map集合,相比Redis,Caffeine的性能更好。Redis是一个分布式缓存系统,独立部署,支持将数据持久化到磁盘上,因此可以在应用程序关闭后仍然保留数据。Redis支持分布式架构,可以配置成主从模式或者集群模式,从而提供更好的水平扩展性和高可用性。
  2. Ehcache和Caffeine的区别?Caffeine是一个较新的本地缓存框架,在内存管理和高并发访问方面通常比Ehcache更高效。

2. 准备

引入依赖即可

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.3</version>
</dependency>

3.代码讲解

Caffeine 最核心的是com.github.benmanes.caffeine.cache.Cache接口,所有与缓存有关的处理方法,都是在这个接口之中定义的,接口中的方法见下图。
在这里插入图片描述

1. 入门代码

private static void demo01() throws InterruptedException {
       
        /**
         * .expireAfterAccess(3L, TimeUnit.SECONDS):假设用户 "Alice" 第一次登录,登录状态被存储在缓存中,并且记录了最后一次访问的时间。
         * 假设用户 "Alice" 第一次登录,登录状态被存储在缓存中,并且记录了最后一次访问的时间。
         * 如果在3秒内没有任何请求使用了 "Alice" 的登录状态,那么缓存中的 "Alice" 条目会在3秒后自动过期,即被移除。
         * 下次有请求需要使用 "Alice" 的登录状态时,缓存会失效,需要重新加载或计算 "Alice" 的登录状态,并将新的状态存储在缓存中
         * 这样设计的好处在于,如果用户在一段时间内没有活动(例如3秒内没有操作),那么缓存中的数据会自动过期,可以确保缓存中的数据不会长时间驻留,
         * 从而减少缓存占用的内存空间,并且确保了数据的及时更新。
         */
        /**
         * 在设置了 .maximumSize(100) 之后,如果缓存中的条目数量超过了100,Caffeine 缓存库会根据一定的策略来进行缓存条目的淘汰,
         * 以确保缓存的大小不会无限增长。
         */
        Cache<String, String> cache = Caffeine.newBuilder()     // 构建一个新的caffeine实例
                                .maximumSize(100)   // 设置缓存之中保存的最大数据量
                                .expireAfterAccess(3L, TimeUnit.SECONDS)    // 缓存数据在3秒内没被访问则失效
                                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        log.info("未超时获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));  // 获取数据,输出:未超时获取缓存数据,Bob= 已登录
        TimeUnit.SECONDS.sleep(5);  // 5秒后超时
        log.info("已超时获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));  // 获取数据,输出:已超时获取缓存数据,Bob= null
        /**
         * 在默认情况下,一旦缓存数据消失之后,Cache 接口可以返回的内容就是 null 数据了,于是有些人认为空数据不利于标注,
         * 那么此时也可以考虑进行一些数据的控制。
         * 这种数据加载操作指的是在缓存数据不存在的时候进行数据的同步加载处理操作
         */
        log.info("已超时获取缓存数据,Bob= {}", cache.get("Bob", (key) -> {  // 最终输出:已超时获取缓存数据,Bob= [expire]Bob
            log.info("失效处理,没有发现 key = {} 的数据,要进行失效处理控制", key);
            return "[expire]" + key;  // 失效数据的返回
        }));
    }

2. 同步数据加载

数据加载是指将数据放入缓存的过程
如果发现指定的 KEY 的缓存项不存在了,Caffeine提供相关功能,实现重新进行数据的加载,例如:通过demo01方法中的操作方法可以发现,此时当缓存数据失效之后,可以自动的根据 Function 函数式接口加载所需要的数据内容(demo01中cache.get(“Bob”, (key) -> { }代码部分)
同步数据加载操作属于同步的操作范畴,加载不停,数据是不会返回的(所有操作均由主线程顺序执行)。而除了上文入门案例demo01中的加载机制之外,在缓存组件之中还提供有一个较为特殊的 CacheLoader 接口,这个接口的触发机制有些不太一样,它所采用的依然是同步的加载处理。

private static void demo02(){
        LoadingCache<String, String> cache = Caffeine.newBuilder()     // 第四步,修改变量类型为LoadingCache
                .maximumSize(100)   // 设置缓存之中保存的最大数据量
                .expireAfterAccess(3L, TimeUnit.SECONDS)    // 缓存数据在3秒内没被访问则失效
                .build(new CacheLoader<String, String>() {  // 第一步,build方法中传参
                    @Override
                    public @Nullable String load(@NonNull String s) throws Exception {
                        log.info("[cacheLoader]进行缓存数据的加载处理, 当前的key = {}", s);
                        TimeUnit.SECONDS.sleep(1);  // 第三步,模拟数据的加载延迟
                        return "【loadingcache】" + s;  // 第二步,数据加载的返回结果
                    }
                });
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        cache.put("Wang", "未登录");

        log.info("未超时获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));  // 未超时获取缓存数据,Bob= 已登录
        try {
            TimeUnit.SECONDS.sleep(5);  // 5秒后超时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cache.put("Lee", "未登录");  // 第五步,缓存失效以后添加新的数据项
        List<String> list = new ArrayList<>();  // 第六步,封装一个list
        list.add("Bob");
        list.add("Lily");
        list.add("Lee");
        for (Map.Entry<String, String> entry : cache.getAll(list).entrySet()){  // 第七步
            log.info("【数据加载】key={},value={}", entry.getKey(), entry.getValue());
        }
        /**
         * 第八步,返回结果
         * 未超时获取缓存数据,Bob= 已登录
         * [cacheLoader]进行缓存数据的加载处理, 当前的key = Bob
         * [cacheLoader]进行缓存数据的加载处理, 当前的key = Lily
         * 【数据加载】key=Bob,value=【loadingcache】Bob  
         * 【数据加载】key=Lily,value=【loadingcache】Lily  
         * 【数据加载】key=Lee,value=未登录
         */
    }

3. 数据的异步加载操作

多线程可以提升性能,优先,功能和上面的同步加载数据相同
数据加载进内存的过程是异步的,从缓存中读数据默认还是由主线程同步实现。
注意:缓存的value应为CompletableFuture.completedFuture(“value”)格式

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        AsyncLoadingCache<String, String> cache = Caffeine.newBuilder()     
                .maximumSize(100)   // 设置缓存之中保存的最大数据量
                .expireAfterAccess(3L, TimeUnit.SECONDS)    // 缓存数据在3秒内没被访问则失效
                .buildAsync((key, executor) ->
                    CompletableFuture.supplyAsync(() -> {
                        log.info("[cacheLoader]进行缓存数据的加载处理, 当前的key = {}", key);
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        return "【loadingcache】" + key;
                    })
                );
        cache.put("Bob", CompletableFuture.completedFuture("已登录"));
        cache.put("Lily",CompletableFuture.completedFuture("未登录"));
        cache.put("Wang", CompletableFuture.completedFuture("未登录"));

        log.info("未超时获取缓存数据,Bob= {}", cache.getIfPresent("Bob").get());  // 未超时获取缓存数据,Bob= 已登录
        try {
            TimeUnit.SECONDS.sleep(5);  // 5秒后超时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cache.put("Lee", CompletableFuture.completedFuture("已登录"));  
        List<String> list = new ArrayList<>(); 
        list.add("Bob");
        list.add("Lily");
        list.add("Lee");
        for (Map.Entry<String, String> entry : cache.getAll(list).get().entrySet()){  
            log.info("【数据加载】key={},value={}", entry.getKey(), entry.getValue());
        }
        /**
         * 结果输出
         * [main] INFO com.cabbage.demos.AsynJiazai - 未超时获取缓存数据,Bob= 已登录
         * [ForkJoinPool.commonPool-worker-2] INFO com.cabbage.demos.AsynJiazai - [cacheLoader]进行缓存数据的加载处理, 当前的key = Bob
         * [ForkJoinPool.commonPool-worker-11] INFO com.cabbage.demos.AsynJiazai - [cacheLoader]进行缓存数据的加载处理, 当前的key = Lily
         * [main] INFO com.cabbage.demos.AsynJiazai - 【数据加载】key=Bob,value=【loadingcache】Bob
         * [main] INFO com.cabbage.demos.AsynJiazai - 【数据加载】key=Lily,value=【loadingcache】Lily
         * [main] INFO com.cabbage.demos.AsynJiazai - 【数据加载】key=Lee,value=已登录
         */

4.缓存数据驱逐

默认的缓存驱逐算法是Window-TinyLFU,提供了最优命中率,有效避免热点数据的失效。
以下代码示例都是基于同步缓存数据加载,

4.1基于缓存容量的驱逐策略

  • 假设缓存容量设置为1,当你设置第二条数据时,第一条数据丢失
private static void demo01() throws InterruptedException {
        Cache<String, String> cache = Caffeine.newBuilder()     // 构建一个新的caffeine实例
                .maximumSize(1)   // 设置缓存之中保存的最大数据量
                .expireAfterAccess(3L, TimeUnit.SECONDS)    // 缓存数据在3秒内没被访问则失效
                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        TimeUnit.MILLISECONDS.sleep(10);
        /**
           有一些延迟
         * 如果不加sleep,会出现
         * 现在缓存个数已经超过了,但是最早的缓存数据还在保留,没有及时清理
         * 大家可以去掉sleep自己试一下
         */
        log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));  // 输出:获取缓存数据,Bob= null
        log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily")); //输出:获取缓存数据,Lily= 未登录
    }

4.2 基于缓存权重驱逐策略

  • 先设置一个总的权重,再为每一条数据定义权重,例如:假设总权重为100,为每条数据设置权重50,那么在你设置第三条数据的时候,会有一条缓存数据被淘汰。
    private static void demo02(){
        Cache<String, String> cache = Caffeine.newBuilder()
                // .maximumSize(100)
                /**
                 * 在进行权重驱逐策略配置的时候,使用的方法为“maximumWeiaht()'
                 * 但是此时不要再设置保存的个数了,
                 * 因为个数的算法和权重的算法是两个不同的方式,二选一的关系。
                 */
                .maximumWeight(100)  // 第一步,设置缓存之中的最大权重
                .weigher((key, value) -> {  // 第二步,权重计算
                    log.info("[weigher权重计算器] key = {}, val = {}", key, value);
                    // 实际开发之中的权重计算处理操作,可以通过KEY和VALUE的长度计算得来
                    return 50; // 第三步
                })
                .expireAfterAccess(3L, TimeUnit.SECONDS)    // 缓存数据在3秒内没被访问则失效
                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        cache.put("Wang","未登录");
        cache.put("Lee","已登录");

        log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));
        log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily"));
        log.info("获取缓存数据,Wang= {}", cache.getIfPresent("Wang"));
        log.info("获取缓存数据,Lee= {}", cache.getIfPresent("Lee"));
        /**
         * 输出
         *  [weigher权重计算器] key = Bob, val = 已登录
         *  [weigher权重计算器] key = Lily, val = 未登录
         *  [weigher权重计算器] key = Wang, val = 未登录
         *  [weigher权重计算器] key = Lee, val = 已登录
         * 获取缓存数据,Bob= null
         * 获取缓存数据,Lily= null
         * 获取缓存数据,Wang= 未登录
         * 获取缓存数据,Lee= 已登录
         */
    }

4.3 基于时间的驱逐策略

  • 在进行驱逐的时候,对于时间的管理有两种,一种是通过最后一次读的方式进行配置(见入门代码.expireAfterAccess(3L, TimeUnit.SECONDS) // 缓存数据在3秒内没被访问则失效),另外一种就是通过写的时间进行计数(写完以后的第几秒,缓存会失效)。
private static void demo03() throws InterruptedException {
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(2L, TimeUnit.SECONDS)    // 第一步,写入后两秒失效
                .build();
        cache.put("Bob", "已登录");
        for (int i = 0;i<3;i++){
            TimeUnit.MILLISECONDS.sleep(1500); // 每次休眠1.5秒
            log.info("[第{}次访问] key = {}, value = {}", i, "Bob", cache.getIfPresent("Bob"));
        }
        /**
         * 输出
         * 14:34:49.972 [main] INFO com.cabbage.demos.CacheEvictionManager - [第0次访问] key = Bob, value = 已登录
         * 14:34:51.478 [main] INFO com.cabbage.demos.CacheEvictionManager - [第1次访问] key = Bob, value = null
         * 14:34:52.989 [main] INFO com.cabbage.demos.CacheEvictionManager - [第2次访问] key = Bob, value = null
         */
    }

4.4 采用定制化的缓存驱逐策略

  • 可以通过 Expiry 接口来实现,这个接口内部定义有如下的处理方法:expireAfterCreate,expireAfterUpdate,expireAfterRead,详细实现参考以下代码
private static void demo04() {
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfter(new Expiry<String, String>() {
                    @Override
                    public long expireAfterCreate(@NonNull String s, @NonNull String s2, long l) {
                        log.info("[创建后失效计算 key = {}, value = {}]", s, s2);
                        // 相当于创建后多少秒就失效了
                        return TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
                    }

                    @Override
                    public long expireAfterUpdate(@NonNull String s, @NonNull String s2, long l, @NonNegative long l1) {
                        log.info("[更新后失效计算 key = {}, value = {}]", s, s2);
                        // 更新完多少秒后就失效了
                        return TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS);
                    }

                    @Override
                    public long expireAfterRead(@NonNull String s, @NonNull String s2, long l, @NonNegative long l1) {
                        log.info("[读取后失效计算 key = {}, value = {}]", s, s2);
                        // 读取完多少秒后就失效了
                        return TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);  // 将2秒转成纳秒
                    }
                })   // 第一步
                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        cache.put("Wang","未登录");
        cache.put("Lee","已登录");

        log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));
        log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily"));
        log.info("获取缓存数据,Wang= {}", cache.getIfPresent("Wang"));
        log.info("获取缓存数据,Lee= {}", cache.getIfPresent("Lee"));

        /**
         *
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [创建后失效计算 key = Bob, value = 已登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [创建后失效计算 key = Lily, value = 未登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [创建后失效计算 key = Wang, value = 未登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [创建后失效计算 key = Lee, value = 已登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [读取后失效计算 key = Bob, value = 已登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - 获取缓存数据,Bob= 已登录
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [读取后失效计算 key = Lily, value = 未登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - 获取缓存数据,Lily= 未登录
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [读取后失效计算 key = Wang, value = 未登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - 获取缓存数据,Wang= 未登录
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - [读取后失效计算 key = Lee, value = 已登录]
         * 14:51:23.040 [main] INFO com.cabbage.demos.CacheEvictionManager - 获取缓存数据,Lee= 已登录
         */
    }

5. 缓存数据的删除与监听

  • 手动删除一条数据
    private static void demo01() {
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        cache.put("Wang","未登录");
        cache.put("Lee","已登录");

        cache.invalidate("Bob");  // 第一步,删除指定key的缓存
        // cache.invalidateAll();  // 删除所有缓存

        log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));
        log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily"));
        log.info("获取缓存数据,Wang= {}", cache.getIfPresent("Wang"));
        log.info("获取缓存数据,Lee= {}", cache.getIfPresent("Lee"));
        /**
         * 输出结果
         * 11:20:37.978 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Bob= null
         * 11:20:37.980 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Lily= 未登录
         * 11:20:37.980 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Wang= 未登录
         * 11:20:37.980 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Lee= 已登录
         */
    }
  • 删除监听,删除数据之前可以通过监听进行一些操作
private static void demo02() {
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .removalListener(new RemovalListener<String, String>() {  // 第一步,设置监听器,删除时触发,removalCause是删除的原因
                    @Override
                    public void onRemoval(@Nullable String s, @Nullable String s2, @NonNull RemovalCause removalCause) {
                        log.info("【数据删除监听】key = {}, value = {}, cause = {}", s, s2, removalCause);
                    }
                })
                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        cache.put("Wang","未登录");
        cache.put("Lee","已登录");

        cache.invalidate("Bob");  // 删除指定key的缓存
        // cache.invalidateAll();  // 删除所有缓存

        log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));
        log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily"));
        log.info("获取缓存数据,Wang= {}", cache.getIfPresent("Wang"));
        log.info("获取缓存数据,Lee= {}", cache.getIfPresent("Lee"));
    }
    /**
     * 输出结果
     * 11:19:46.021 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Bob= null
     * 11:19:46.021 [ForkJoinPool.commonPool-worker-9] INFO com.cabbage.demos.CacheDelAndListener - 【数据删除监听】key = Bob, value = 已登录, cause = EXPLICIT
     * 11:19:46.024 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Lily= 未登录
     * 11:19:46.024 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Wang= 未登录
     * 11:19:46.024 [main] INFO com.cabbage.demos.CacheDelAndListener - 获取缓存数据,Lee= 已登录
     */

6. CacheStats 缓存数据统计

/**
     * 获取缓存的 统计数据
     * 在使用数据统计的时候,Caffeine 内部使用了一个 StatsCounter 接口类型,
     * 最终如果要想实现数据的统计的处理操作,那么肯定是需要通过 StatsCounter 接口实现的,而这个接口提供有一个内置的并
     * 发数据统计的操作实现子类。
     * Cafeine 缓存组件除了提供有强大的缓存处理性能之外,也额外提供了一些缓存数据的统计功能,每当用户进行缓存数据操下时,
     * 都可以对这些操作记录的结果进行记录,这样就可以准确的知道缓存命中数、失效数、驱逐数等统计结果。
     */
    private static void demo01() throws InterruptedException {
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterAccess(1L, TimeUnit.MILLISECONDS)  // 设置1毫秒未读过期
                .recordStats()
                .build();
        cache.put("Bob", "已登录");
        cache.put("Lily","未登录");
        cache.put("Wang","未登录");
        cache.put("Lee","已登录");

        // 此时设置的候选的KEY数据是有些不存在的,通过这些不存在的数据进行最终的非命中统计操作
        String[] keys = new String[]{"Bob", "Lily", "Wang", "Lee", "No1", "no2"};
        // 定义随机数
        Random random = new Random();
        for (int i=0;i<1000;i++){
            new Thread(() -> {
                String key = keys[random.nextInt(keys.length)]; // 随机选取一个key
                log.info("key = {}, value = {}", key,cache.getIfPresent(key));
            },"查询线程 - "+i).start();
        }
        TimeUnit.SECONDS.sleep(1);  // 让多线程执行完
        CacheStats stats = cache.stats();
        log.info("【CacheStats】缓存操作请求次数: {}", stats.requestCount());
        log.info("【CacheStats】缓存命中次数: {}", stats.hitCount());
        log.info("【Cachestats】缓存未命中次数: {}", stats.missCount());
        //所有的缓存组件里面,最为重要的一项性能指标就是命中率的处理问题了
        log.info("【CacheStats】缓存命中率: {}", stats.hitRate());
        log.info("【CacheStats】缓存驱逐次数: {}", stats.evictionCount());
        /**
         * 输出结果
         16:20:02.911 [main] INFO com.cabbage.demos.CacheStatsDemo - 【CacheStats】缓存操作请求次数: 1000
         16:20:02.911 [main] INFO com.cabbage.demos.CacheStatsDemo - 【CacheStats】缓存命中次数: 13
         16:20:02.911 [main] INFO com.cabbage.demos.CacheStatsDemo - 【Cachestats】缓存未命中次数: 987
         16:20:02.911 [main] INFO com.cabbage.demos.CacheStatsDemo - 【CacheStats】缓存命中率: 0.013
         16:20:02.911 [main] INFO com.cabbage.demos.CacheStatsDemo - 【CacheStats】缓存驱逐次数: 4

         */
    }
Caffeine本地缓存适用于以下场景: 1. 提升应用性能:Caffeine本地缓存可以将频繁访问的数据缓存在内存中,以减少对底层数据源的访问次数,从而提高应用的性能和响应速度。 2. 降低系统负载:通过使用Caffeine本地缓存,可以避免频繁地从数据库或其他外部数据源中读取数据,减少了对外部系统的访问,从而降低了系统的负载。 3. 数据共享与共享状态管理:Caffeine本地缓存可以用于在应用内部共享数据,减少重复计算和查询的开销。同时,它也可以用于管理应用中的共享状态,确保多个线程或进程之间的数据一致性。 4. 缓存数据的有效期管理:Caffeine本地缓存提供了对缓存数据有效期的管理,可以根据需求设置缓存过期时间,以确保缓存数据的及时更新和一致性。 5. 缓解外部服务的压力:对于一些需要频繁调用外部服务的场景,可以使用Caffeine本地缓存缓存外部服务的响应结果,减少对外部服务的调用次数,降低对外部服务的压力。 总之,Caffeine本地缓存适用于需要提升应用性能、降低系统负载、数据共享与共享状态管理、缓存数据有效期管理和缓解外部服务压力的场景。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Caffeine一级缓存介绍和应用](https://blog.youkuaiyun.com/u011507134/article/details/127107322)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值