Java guava框架LoadingCache及CacheBuilder本地小容量缓存框架总结

 更新时间:2023年12月22日 11:56:09   作者:极光雨雨  
Guava Cache本地缓存框架主要是一种将本地数据缓存到内存中,但数据量并不能太大,否则将会占用过多的内存,本文给大家介绍Java guava框架 LoadingCache及CacheBuilder 本地小容量缓存框架总结,感兴趣的朋友一起看看吧

Guava Cache本地缓存框架介绍

主要是一种将本地数据缓存到内存中,但数据量并不能太大,否则将会占用过多的内存,虽然框架本身已经做了相当的数据回收,但还是不可以滥用,需要符合以下优点场景,才是合适使用,访问内存的速度快于访问 redis 等数据库。

有点以及需求场景:

  • 对性能有非常高的要求
  • 愿意消耗一些内存空间来提升速度
  • 预计到某些键会被多次查询
  • 缓存中存放的数据总量不会超出内存容量

关键点是:有频繁访问的数据,且这些数据本身占用内存量很少,将这些数据存储到该缓存框架中管理以提供性能

提供的优势能力

  • 缓存可以设置过期时间,并提供数据过多时的淘汰机制
  • 是线程安全的,支持并发读入和写入
  • 缓存获取不到时可以从数据源获取并加入到缓存中,GuavaCache 可以使用 CacheLoader 的load 方法控制,对同一个key,只允许一个请求去读源并回填缓存,其他请求阻塞等待
  • 可以查看缓存的加载获取信息等

使用以及方法

Maven 依赖:

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>

最简单的构建案例

Cache<String, Object> cache = CacheBuilder.newBuilder().build();
cache.put("aaa", 156484);

Map类似,获取时使用get() 方法即可获取到放入其中的数据。

通过 CacheBuilder 创建 Cache 对象,存储类似于Map 构建方法为链式构造,类似于 builder 建造者模式,返回均为当前对象本身,调用 build 方法后结束构造。

CacheBuilder.newBuilder() 后的一些构建参数方法介绍:

initialCapacity:缓存的初始数据容量大小,一般要贴合实际否则会造成资源浪费
maximumSize:缓存中可包含最大 entry 数量,超过数量限制后淘汰 entry,接近最大值时淘汰不常用数据,设置为 0 时为不使用缓存的场景,用于测试数据加载

过期时间设置

expireAfterAccess:数据写入后被访问对象多久没被访问认为过期
expireAfterWrite: 数据被写入到缓存后一直未更新多久后过期

可以如下写:

        Cache<String, Object> cache = CacheBuilder.newBuilder()
                .initialCapacity(5)
                .maximumSize(10)
                .expireAfterWrite(10, TimeUnit.MINUTES).build();
		cache.put("154", "djawbdawd");

过期时间单位通过 TimeUnit 后的控制,时分秒均可

弱软引用 (weakKeys, weakValues, softValues)

weakKeys
将缓存中的key设置成weakKey模式。
默认情况下,会使用“强关系”来保存key值。当设置为weakKey时,会使用 “==” 来匹配key值。在使用weakKey的情况下,数据可能会被GC。数据被GC后,可能仍然会被size方法计数,但是对其执行read或write方法已经无效。

weakValues
将缓存中的数据设置为weakValues模式。
启用时,某些数据会被GC。默认情况下会使用“强关系”来保存key值。当设置为weakValue时,会使用 “==” 来匹配value值。数据被GC后,可能仍然会被size方法计数,但是对其执行read或write方法已经无效。

softValues
将缓存中的数据设置为softValues模式。
启用时,所有的数据都使用SoftReference类对缓存中的数据进行包裹(就是在SoftReference实例中存储真实的数据)。使用SoftReference包裹的数据,会被全局垃圾回收管理器托管,按照LRU的原则来定期GC数据。数据被GC后,可能仍然会被size方法计数,但是对其执行read或write方法已经无效。

这些同样在 CacheBuilder.newBuilder() 后设置。

主动删除数据

当通过 builder 创建对象完成后,可以通过以下方式清除数据:
invalidateAll:清除全部数据,入参为 Iterable 类型参数,即 一般的List 集合即可,内容为所有的 key
invalidate:单一删除,入参为 key

删除监听器

即用于监控缓存中的数据,当数据被删除时会被触发
类似如下代码:

        RemovalListener<String, String> listener = new RemovalListener<String, Object>() {
            public void onRemoval(RemovalNotification<String, String> notification) {
            // notification.getKey();  // 当前删除对象的 key
            // notification.getValue();  // 当前删除对象的 value
            }
        };
        Cache<String, Object> cache = CacheBuilder.newBuilder()
                .maximumSize(5)
                // 添加移除监听器
                .removalListener(listener) 
                .build();

值得注意的是,这里的删除不仅是主动删除,当达到容量上限或者过期或由于其他策略导致数据消失时也认为是删除

一般cache 主动加载数据

即当缓存中获取不到指定 key 对应的 value 时,需要主动去其他途径获取:

写法类似如下,需要提供获取的实现方法:

Cache<String, Object> cache = CacheBuilder.newBuilder()
                .initialCapacity(5)
                .maximumSize(10)
                .expireAfterWrite(10, TimeUnit.MINUTES).build();
        cache.put("154", "djawbdawd");
        try {
            cache.get("aaa", new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    // 具体如何获取
                    // 方法的返回值即为 当前key 对应的value 并将会加入到缓存中
                    return null;
                }
            });
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

需要在 Callable 内的方法重写获取实际数据的方法,方法的返回值即为 当前key 对应的value 并将会加入到缓存中,同时如果存在多个线程同时获取同一个不存在的 key,那么将只有一个被执行,其他需要等待。且一个线程获取后,其他线程将也可以获取这些数据。

统计信息

创建cache 对象时调用**.recordStats()** 后将开启统计,可以通过 cache.stats() 获取缓存
例如:

        Cache<String, Object> cache = CacheBuilder.newBuilder()
                .initialCapacity(5)
                .maximumSize(10)
                .recordStats()
                .expireAfterWrite(10, TimeUnit.MINUTES).build();
        cache.put("154", "djawbdawd");
        try {
            cache.get("aaa", new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    // .......
                    return null;
                }
            });
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        cache.stats();

较常用的 LoadingCache

LoadingCache 是 Cache的子接口 ,相比于Cache,当从LoadingCache中读取一个指定key的记录时,如果该记录不存在,则LoadingCache可以自动执行加载数据到缓存的操作,定义时需要重写加载方法。
LoadingCache接口的定义如下:

CacheLoader<String, String> loader = new CacheLoader<>() {
		// 加载没有的数据的方法
            public String load(String key) throws Exception {
            // .............
                return null;
            }
        };
        LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
                .maximumSize(5)
                .build(loader);

或如下:

LoadingCache<String, Object> infoItemCache = CacheBuilder.newBuilder()
		.initialCapacity(5)
		.maximumSize(10)
		.expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
                    // 使用 LoadingCache 重写加载方法, 重写后当无法从缓存中获取到key 对应的值时将执行重写的load 方法,且返回值将成为入参key 对应的Value并存储
                    @Override
                    public Object load(String s) throws Exception {
                        // ............
                        return null;
                    }
                });

可以自定义简单工具如下:

import com.dtdream.dthink.dtalent.dmall.dataresource.catalog.model.metadata.MetadataBlock;
import com.dtdream.dthink.dtalent.dmall.dataresource.catalog.service.catalog.ICatalogTemplateService;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class MyCache {
    /**
     * 初始化缓存容量大小
     */
    private static final int INITIAL_CAPACITY = 5;
    /**
     * 缓存中可包含最大 entry 数量,超过数量限制后淘汰 entry,接近最大值时淘汰不常用数据,设置为 0 时为不使用缓存的场景,用于测试数据加载
     */
    private static final long MAXIMUM_SIZE = 10;
    /**
     * 放入缓存后指定时间内没有被访问将过期
     */
    private static final long EXPIRE_AFTER_ACCESS = 5;
    /**
     * cache 子接口, 缓存对象
     */
    private static LoadingCache<String, Object> infoItemCache;
    // 初始化缓存对象
    static {
        infoItemCache = CacheBuilder.newBuilder().initialCapacity(INITIAL_CAPACITY).maximumSize(MAXIMUM_SIZE)
                .expireAfterAccess(EXPIRE_AFTER_ACCESS, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
                    // 使用 LoadingCache 重写加载方法, 重写后当无法从缓存中获取到key 对应的值时将执行重写的load 方法,且返回值将成为入参key 对应的Value并存储
                    @Override
                    public Object load(String s) throws Exception {
                        // ...............
                        return null;
                    }
                });
    }
    /**
     * 从缓存中获取数据,若不存在则重新查询
     * @return
     */
    public static Object getFromCache(String key) throws ExecutionException {
        Object data = infoItemCache.get(key);
        return data;
    }
}

若需要从Spring 获取Bean 服务,则可以通过 Spring 的 applicationConext 去获取

到此这篇关于Java guava框架 LoadingCache,CacheBuilder 本地小容量缓存框架学习以及总结的文章就介绍到这了,更多相关Java guava框架 LoadingCache内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Spring @Autowired 注入小技巧

    详解Spring @Autowired 注入小技巧

    这篇文章主要介绍了详解Spring @Autowired 注入小技巧,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • SpringBoot无法解析parameter参数问题的解决方法

    SpringBoot无法解析parameter参数问题的解决方法

    使用最新版的 Springboot 3.2.1(我使用3.2.0)搭建开发环境进行开发,调用接口时出现奇怪的错,本文小编给大家介绍了SpringBoot无法解析parameter参数问题的原因及解决方法,需要的朋友可以参考下
    2024-04-04
  • 剑指Offer之Java算法习题精讲二叉树与链表

    剑指Offer之Java算法习题精讲二叉树与链表

    跟着思路走,之后从简单题入手,反复去看,做过之后可能会忘记,之后再做一次,记不住就反复做,反复寻求思路和规律,慢慢积累就会发现质的变化
    2022-03-03
  • Java将byte[]转图片存储到本地的案例

    Java将byte[]转图片存储到本地的案例

    这篇文章主要介绍了Java将byte[]转图片存储到本地的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • Java 电话号码的组合示例详解

    Java 电话号码的组合示例详解

    这篇文章主要介绍了Java 电话号码的组合,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Java均摊复杂度和防止复杂度的震荡原理分析

    Java均摊复杂度和防止复杂度的震荡原理分析

    这篇文章主要介绍了Java均摊复杂度和防止复杂度的震荡,结合实例形式分析了Java均摊复杂度和防止复杂度的震荡相关概念、原理、实现方法与注意事项,需要的朋友可以参考下
    2020-03-03
  • 详解Java中NullPointerException异常的原因和解决办法

    详解Java中NullPointerException异常的原因和解决办法

    本文主要介绍了详解Java中NullPointerException异常的原因和解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • SpringBoot项目实现通过分词器生成词云

    SpringBoot项目实现通过分词器生成词云

    本文介绍了如何在SpringBoot项目中利用IK分词器和Kumo库生成员工任务的词云图,展示了从引入依赖、配置YAML到Controller中处理文本并生成词云的详细步骤以及处理中文文本的注意事项,希望对大家有所帮助
    2026-05-05
  • Java数组看这篇就够了

    Java数组看这篇就够了

    这篇文章主要介绍了Java数组的详细解释,是Java入门学习中的基础知识,需要的朋友可以参考下,希望能够给你带来帮助
    2021-09-09
  • SpringBoot + Redis 实现API接口限流的几种方法

    SpringBoot + Redis 实现API接口限流的几种方法

    本文主要介绍了SpringBoot + Redis 实现API接口限流的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-01-01

最新评论