Guava Cache 介绍

这篇具有很好参考价值的文章主要介绍了Guava Cache 介绍。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Guava 是 Google 提供的一套 Java 工具包,而 Guava Cache 是该工具包中提供的一套完善的 JVM 级别高并发缓存框架;本文主要介绍它的相关功能及基本使用,文中所使用到的软件版本:Java 1.8.0_341、Guava 32.1.3-jre。

1、简介

缓存在很多情况下非常有用。例如,当某个值的计算或检索代价很高,并且你需要在特定输入下多次使用该值时,就应该考虑使用缓存。

Guava Cache 与 ConcurrentMap 类似,但并不完全相同。最基本的区别在于,ConcurrentMap 会一直保存所有添加到其中的元素,直到显式地将它们删除。而 Guava Cache 通常会配置自动删除条目,以限制其内存占用。在某些情况下,即使不删除条目,LoadingCache 也很有用,因为它具有自动加载条目的功能。

通常情况下,Guava Cache 适用于以下情况:

  • 你愿意花费一些内存来提高速度。
  • 你预期某些键有时会被多次查询。
  • 你的缓存不需要存储超过内存容量的数据。(Guava Cache 是局限于应用程序运行期间的本地缓存。它们不会将数据存储在文件或外部服务器上。如果这不符合你的需求,可以考虑使用像Memcached 这样的工具。)

如果你的情况符合上述每一点,那么 Guava Cache 可能适合你。

注意:如果你不需要缓存的特性,ConcurrentHashMap 在内存效率方面更高——但是使用任何 ConcurrentMap 几乎不可能复制大多数 Guava Cache 的特性。

2、数据加载

使用 Guava Cache 时,首先要问自己一个问题:是否有合理的默认函数来加载或计算需缓存的数据?如果是这样,应该使用 CacheLoader。如果没有,或者需要覆盖默认函数,但仍然希望具有原子的“如果不存在则计算并获取”语义,你应该将一个 Callable 对象传递给 get 方法。可以直接使用 Cache.put 方法插入元素,但更推荐自动加载数据,因为这样可以更容易地推断所有缓存内容的一致性。

2.1、CacheLoader

LoadingCache 是一个带有 CacheLoader 的缓存。创建 CacheLoader 很容易,只需要实现方法 V load(K key) throws Exception 即可。

LoadingCache<Long, String> loadingCache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .build(new CacheLoader<Long, String>() {
            @Override
            public String load(Long key) throws Exception {
                //TODO: 根据业务加载数据
                return RandomStringUtils.randomAlphanumeric(10);
            }
        });
try {
    log.info(loadingCache.get(1L));
} catch (ExecutionException e) {
    e.printStackTrace();
}

LoadingCache 使用 get(K) 方法来获取数据。该方法要么返回已缓存的值,要么使用 CacheLoader 来原子地加载一个新值到缓存中。由于 CacheLoader 可能会抛出异常,LoadingCache.get(K) 方法会抛出 ExecutionException 异常。(如果 CacheLoader 抛出未经检查异常,get(K) 方法将抛出包装异常 UncheckedExecutionException)。也可以选择使用 getUnchecked(K) 方法,它将所有异常都包装在UncheckedExecutionException 中,但如果底层的 CacheLoader 抛出已检查异常,这可能导致意外行为。

LoadingCache<Long, String> loadingCache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .build(new CacheLoader<Long, String>() {
            @Override
            public String load(Long key) {//抛出未检查异常
                //TODO: 根据业务加载数据
                return RandomStringUtils.randomAlphanumeric(10);
            }
        });
log.info(loadingCache.getUnchecked(1L));

可以使用 getAll(Iterable<? extends K>)方法执行批量查询。默认情况下,getAll 会为缓存中不存在的每个键单独调用 CacheLoader.load 方法。当批量检索比多个单独查找更高效时,可以重写CacheLoader.loadAll 以利用此功能。getAll(Iterable)的性能将相应提高。

2.2、Callable

所有 Guava Cache,无论是 LoadingCache 还是非 LoadingCache,都支持 get(K, Callable<V>) 方法。该方法返回与缓存中键相关联的值,或者从指定的 Callable 计算它并将其添加到缓存中。在加载完成之前,与此缓存关联的任何可观察状态都不会被修改。该方法为传统的“如果有缓存,则返回;否则创建、缓存并返回”模式提供了一个简单的替代方案。

Cache<Long, String> cache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .build();
try {
    String s = cache.get(1L, new Callable<String>() {
        @Override
        public String call() throws Exception {
            //TODO: 根据业务加载数据
            return RandomStringUtils.randomAlphanumeric(10);
        }
    });
    log.info(s);
} catch (ExecutionException e) {
    e.printStackTrace();
}

2.3、直接插入

可以使用 cache.put(key, value) 方法直接将值插入到缓存中。这会覆盖缓存中指定键的的任何先前条目。还可以使用 Cache.asMap() 视图公开的任何 ConcurrentMap 方法更改缓存。请注意,asMap 视图上的任何方法都不会自动将条目加载到缓存中。此外,视图上的原子操作在缓存自动加载的范围之外运行,因此在使用 CacheLoader 或 Callable 加载值的缓存中,始终应优先选择 Cache.get(K, Callable<V>) 而不是 Cache.asMap().putIfAbsent()。

3、数据淘汰

现实情况是,我们几乎肯定没有足够的内存来缓存所有可能的内容。你必须决定:什么时候不值得保留缓存条目?Guava提供了三种数据淘汰方式:基于大小的淘汰、基于时间的淘汰和基于引用的淘汰。

3.1、基于容量的淘汰

 如果你的缓存不应该超过一定大小,只需使用 CacheBuilder.maximumSize(long) 。缓存将尝试淘汰最近未被使用或使用频率很低的条目。警告:在达到限制之前,缓存可能会淘汰条目,通常是在缓存大小接近限制时。

或者,如果不同的缓存条目具有不同的“权重”——例如,如果你的缓存值具有截然不同的内存占用,你可以使用 CacheBuilder.weigher(Weigher) 来指定一个权重函数,并使用CacheBuilder.maximumWeight(long) 来设置最大的缓存权重。除了与 maximumSize 相同的注意事项外,请注意权重是在条目创建时计算的,并且在此后是静态的。

Cache<Long, String> cache = CacheBuilder.newBuilder()
        .maximumWeight(100000)
        .weigher(new Weigher<Long, String>() {
            @Override
            public int weigh(Long key, String value) {
                return value.getBytes().length;
            }
        }).build();

3.2、基于时间的淘汰

CacheBuilder 提供了两种基于时间淘汰数据的方法:

expireAfterAccess(long, TimeUnit):在最后一次读取或写入条目后,仅在指定的持续时间过去后才淘汰条目。需要注意的是,条目的淘汰顺序类似于基于大小的淘汰策略。
expireAfterWrite(long, TimeUnit):在条目创建或最近一次替换值之后,仅在指定的持续时间过去后才淘汰条目。如果缓存数据在一段时间后变得过时,这种方式可能是可取的。

3.3、基于引用的淘汰

Guava 允许通过对键或值使用弱引用和对值使用软引用来设置缓存,从而利用垃圾回收来淘汰数据。

  • CacheBuilder.weakKeys() 使用弱引用来存储键。这意味着当键没有其他(强或软)引用时,条目可以被垃圾回收。由于垃圾回收仅依赖于内存地址相等性,这导致整个缓存使用(==)来比较键,而不是equals()方法。
  • CacheBuilder.weakValues() 使用弱引用来存储值。这意味着当值没有其他(强或软)引用时,条目可以被垃圾回收。由于垃圾回收仅依赖于内存地址相等性,这导致整个缓存使用(==)来比较值,而不是equals()方法。
  • CacheBuilder.softValues() 使用软引用来存储值。以软引用方式引用的对象会根据内存需求以全局最近最少使用的方式进行垃圾回收。由于使用软引用可能会影响性能,我们通常建议使用更可预测的 maximum cache size 替代。使用 softValues() 将导致值使用(==)来比较,而不是 equals() 方法。

3.4、显式删除

在任何时候,你可以显式地使缓存条目失效,而不是等待条目被淘汰。可以通过以下方式实现:

单个失效:使用 Cache.invalidate(key)
批量失效:使用 Cache.invalidateAll(keys)
全部失效:使用 Cache.invalidateAll()

3.5、删除监听器

你可以为缓存指定一个删除监听器(RemovalListener),以在条目被移除时执行某些操作,通过 CacheBuilder.removalListener(RemovalListener) 方法指定删除监听器。RemovalListener 会接收到一个RemovalNotification 对象,其中包含了 RemovalCause、键和值的信息。

需要注意的是,任何由 RemovalListener 抛出的异常都会被记录(使用 Logger 时)并被忽略。

Cache<Long, String> cache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .removalListener(new RemovalListener<Long, String>() {
            @Override
            public void onRemoval(RemovalNotification<Long, String> notification) {
                log.info(notification.toString());
            }
        })
        .build();

警告:默认情况下,移除监听器操作是同步执行的。由于缓存维护通常在正常缓存操作期间执行,因此移除监听器可能会降低缓存的速度!如果需要移除监听器,请使用RemovalListeners.asynchronous(RemovalListener, Executor) 方法来装饰 RemovalListener,这样可以以异步的方式运行。

3.6、数据清理时机

使用 CacheBuilder 构建的缓存不会“自动”执行清理和逐出值,也不会在值过期后立即执行清理和逐出值,也不会执行任何类似操作。相反,它会在写入操作期间执行少量维护,或者在偶尔的读取操作期间(如果写入很少)执行少量维护。

原因是:如果我们想要连续执行缓存维护,我们需要创建一个线程,它的操作将与用户操作竞争共享锁。此外,某些环境限制了线程的创建,这将使 CacheBuilder 在该环境中无法使用。

相反,我们将选择权交到你手中。如果你的缓存是高吞吐量的,那么你无需担心执行缓存维护来清除过期条目等问题。如果你的缓存只偶尔进行写操作,并且不想让清理阻塞缓存读取,你可以创建自己的维护线程,定期调用 Cache.cleanUp() 方法。

如果要为很少进行写入操作的缓存安排定期缓存维护,请使用 ScheduledExecutorService。

3.7、刷新

刷新(Refreshing)与淘汰(Eviction)并不完全相同。根据 LoadingCache.refresh(K) 的定义,刷新一个键会加载该键的新值,这可能是异步的。在键正在刷新的过程中,旧值(如果存在)仍然会被返回,这与淘汰操作不同,淘汰操作会导致获取操作等待直到新值加载完成。

如果在刷新过程中发生异常,旧值将被保留,异常将被记录并忽略。

可以根据业务需要,重写 CacheLoader 的 CacheLoader.reload(K, V) 方法来重新定义刷新操作;这允许你在计算新值时使用旧值。

LoadingCache<Integer, String> loadingCache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .refreshAfterWrite(1, TimeUnit.MINUTES)
        .build(new CacheLoader<Integer, String>() {
            @Override
            public String load(Integer key) throws Exception {
                //TODO: 根据业务加载数据
                return RandomStringUtils.randomAlphanumeric(10);
            }

            @Override
            public ListenableFuture<String> reload(Integer key, String oldValue) throws Exception {
                if (neverNeedsRefresh(key)) {//不需要刷新
                    return Futures.immediateFuture(oldValue);
                } else {
                    //异步刷新
                    ListenableFutureTask<String> task = ListenableFutureTask.create(new Callable<String>() {
                        public String call() {
                            return RandomStringUtils.randomAlphanumeric(10);
                        }
                    });
                    executorService.execute(task);
                    return task;
                }
            }
        });

可以使用 CacheBuilder.refreshAfterWrite(long, TimeUnit) 为缓存添加自动定时刷新。与 expireAfterWrite 不同,refreshAfterWrite 会使一个键在指定的时间后变为可刷新状态,但只有在查询该条目时才会实际启动刷新(如果 CacheLoader.reload 被实现为异步,则查询不会因刷新而变慢)。因此,可以在同一个缓存上同时指定 refreshAfterWrite 和 expireAfterWrite,以便在条目变为可刷新状态时不会盲目地重置过期计时器,如果一个条目在变为可刷新状态后没有被查询,它就允许过期。

4、特点

4.1、统计信息

使用 CacheBuilder.recordStats() 可以为 Guava Cache 打开统计信息收集功能。Cache.stats() 方法返回一个 CacheStats 对象,该对象提供了诸如以下统计信息:

  • hitRate():返回命中次数与请求次数之比。
  • averageLoadPenalty():平均加载新值所花费的时间(以纳秒为单位)。
  • evictionCount():缓存淘汰的数量。

还有许多其他的统计信息。这些统计信息在缓存调优中非常重要,我们建议在性能关键的应用程序中密切关注这些统计信息。

4.2、asMap

可以使用 Cache 的 asMap 视图将任何缓存视为 ConcurrentMap,但是 asMap 视图与缓存的交互需要一些说明。

  • cache.asMap() 包含当前加载在缓存中的所有条目。例如,cache.asMap().keySet() 包含当前加载的所有键。
  • asMap().get(key) 基本等同于 cache.getIfPresent(key),并且不会导致值被加载。这与 Map 的约定一致。
  • 访问时间会被读取和写入操作重置(包括 Cache.asMap().get(Object) 和 Cache.asMap().put(K, V)),但不会被 containsKey(Object) 或其他操作所重置。因此,遍历 cache.asMap().entrySet() 不会重置条目的访问时间。

5、简单使用

5.1、引入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

5.2、简单使用

public static void main(String[] args) {
    LoadingCache<Long, String> loadingCache = CacheBuilder.newBuilder()
            .initialCapacity(1000)
            .maximumSize(10000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .refreshAfterWrite(3, TimeUnit.MINUTES)
            .recordStats()
            .build(new CacheLoader<Long, String>() {
                @Override
                public String load(Long key) throws Exception {//抛出已检查异常
                    //TODO: 根据业务加载数据
                    return RandomStringUtils.randomAlphanumeric(10);
                }
            });
    try {
        log.info(loadingCache.get(1L));
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

 

 

参考:https://github.com/google/guava/wiki/CachesExplained。文章来源地址https://www.toymoban.com/news/detail-746082.html

到了这里,关于Guava Cache 介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 仪酷LabVIEW AI视觉工具包及开放神经网络交互工具包常见问题解答

    哈喽,各位朋友,好久不见~ 之前给大家分享了基于LabVIEW开发的AI视觉工具包及开放神经网络交互工具包,不少朋友私信说在安装和使用过程中会遇到一些问题,今天我们就集中回复一下大家问到最多的问题。如果大家在使用过程中还有其他问题,可以补充到评论区,我们这

    2024年02月16日
    浏览(79)
  • GIS工具包

    GIS工具包,根据jts工具,结合实际使用场景提取出来的常用工具集合;涵盖几何格式转换(WKT,GeoJSON等)与geometry转换、gis距离计算、度距离单位换算、角度计算、buffer运算、映射截取、几何穿串等操作 gis-tools源码库地址 1.1 WktTool使用说明 wkt格式与geometry互转; wkt转geometry操作

    2024年02月06日
    浏览(88)
  • 学习笔记-JVM-工具包(JVM分析工具)

    常用工具 JDK工具 ① jps: JVM Process status tool:JVM进程状态工具,查看进程基本信息 ② jstat: JVM statistics monitoring tool : JVM统计监控工具,查看堆,GC详细信息 ③ jinfo:Java Configuration Info :查看配置参数信息,支持部分参数运行时修改 ④ jmap:Java Memory Map :分析堆内存工具,du

    2024年02月13日
    浏览(69)
  • MATLAB添加工具包(详细)

    我这里要添加的文件包为:DeepLearnToolbox-master 我这里的安装目录是:D:softwareMATLABtoolbox (1)以中文版为例,在主界面找到“设置路径”按钮 (2)点击“设置路径”,弹出设置界面 第一步:点“添加文件夹” (注:如果要工具包中有多个子文件夹,则点“添加并包含子文

    2024年02月02日
    浏览(79)
  • Quanto: PyTorch 量化工具包

    量化技术通过用低精度数据类型 (如 8 位整型 (int8)) 来表示深度学习模型的权重和激活,以减少传统深度学习模型使用 32 位浮点 (float32) 表示权重和激活所带来的计算和内存开销。 减少位宽意味着模型的内存占用更低,这对在消费设备上部署大语言模型至关重要。量化技术也

    2024年04月10日
    浏览(89)
  • Hardhat工具包1--安装使用

    参考资料: 官方文档 : https://hardhat.org/getting-started/ https://hardhat.org/hardhat-runner/docs/getting-started#overview 基于Hardhat和Openzeppelin开发可升级合约(一) 基于Hardhat和Openzeppelin开发可升级合约(一)_灬倪先森_的博客-CSDN博客 ---------------------------------------------------------------------------------

    2023年04月11日
    浏览(125)
  • 【Linux】基本开发工具包使用

    目录 一, yum ——linux软件包管理器  1. 软件包是啥子?  2.  yum基本使用  1. 步骤:  2. 开发工具推荐(centos 7.6) 二,vim —— linux文本编辑器 1. Normal mode  ——  命令模式(记不住没关系,多练就行) 2.  last line  mode——   末行模式 (如何进入;shift :) 3. Insert mode ——插

    2024年02月08日
    浏览(81)
  • Windows11渗透工具包分享

              项目地址 下载地址

    2024年02月13日
    浏览(73)
  • NetAssist网络调试工具使用指南 (附NetAssist工具包)

    1、NetAssist简介 NetAssist网络调试助手,是Windows平台下开发的TCP/IP网络调试工具,集TCP/UDP服务端及客户端于一体,是网络应用开发及调试工作必备的专业工具之一,可以帮助网络应用设计、开发、测试人员检查所开发的网络应用软/硬件产品的数据收发状况,提高开发速度,简

    2024年02月16日
    浏览(70)
  • Kubernetes GoRoutineMap工具包代码详解

    GoRoutineMap 定义了一种类型,可以运行具有名称的 goroutine 并跟踪它们的状态。它防止创建具有相同名称的多个goroutine,并且在上一个具有该名称的 goroutine 完成后的一段退避时间内可能阻止重新创建 goroutine。 使用GoRoutineMap场景: 使用协程的方式运行函数逻辑,如果函数成功

    2024年02月06日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包