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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java String字符串补0或空格的实现代码

    Java String字符串补0或空格的实现代码

    这篇文章主要介绍了Java String字符串补0或空格的实现代码,代码简单易懂,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
    2016-09-09
  • RSA加密算法java简单实现方法(必看)

    RSA加密算法java简单实现方法(必看)

    下面小编就为大家带来一篇RSA加密算法java简单实现方法(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • Java Calendar类使用案例详解

    Java Calendar类使用案例详解

    这篇文章主要介绍了Java Calendar类使用案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 深入分析JAVA 多线程--interrupt()和线程终止方式

    深入分析JAVA 多线程--interrupt()和线程终止方式

    这篇文章主要介绍了JAVA 多线程--interrupt()和线程终止方式的的相关资料,文中代码非常细致,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • Java中与数字相关的常用类的用法详解

    Java中与数字相关的常用类的用法详解

    在我们的代码中,经常会遇到一些数字&数学问题、随机数问题、日期问题和系统设置问题等,为了解决这些问题,Java给我们提供了多个处理相关问题的类,比如Number类、Math类、Random类等等,本篇文章我们先从Number数字类和Math数学类学起
    2023-05-05
  • Java函数式编程(九):Comparator

    Java函数式编程(九):Comparator

    这篇文章主要介绍了Java函数式编程(九):Comparator,本文是系列文章的第9篇,其它文章请参阅本文底部的相关文章,需要的朋友可以参考下
    2014-09-09
  • Spring Security实现自定义访问策略

    Spring Security实现自定义访问策略

    本文介绍Spring Security实现自定义访问策略,当根据谁访问哪个域对象做出安全决策时,您可能需要一个自定义的访问决策投票者,幸运的是,Spring Security有很多这样的选项来实现访问控制列表(ACL)约束,下面就来学习Spring Security自定义访问策略,需要的朋友可以参考下
    2022-02-02
  • Java语言简介(动力节点Java学院整理)

    Java语言简介(动力节点Java学院整理)

    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征,下面通过本文给大家分享java语言的简介,感兴趣的朋友一起看看吧
    2017-03-03
  • Spring中的@Value和@PropertySource注解详解

    Spring中的@Value和@PropertySource注解详解

    这篇文章主要介绍了Spring中的@Value和@PropertySource注解详解,@PropertySource:读取外部配置文件中的key-value保存到运行的环境变量中,本文提供了部分实现代码,需要的朋友可以参考下
    2023-11-11
  • 如何集成swagger2构建Restful API

    如何集成swagger2构建Restful API

    这篇文章主要介绍了如何集成swagger2构建Restful API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11

最新评论