cache组件中核心的类和接口列举如下:
接口:

  • Cache 本地缓存的顶级接口,提供一些对缓存进行get,put的方法,以及获取缓存统计数据的方法等。
  • LoadingCache 继承了Cache接口,并另外提供了一些当get数据不存在时自动去load相关key(s)所对应的value(s)的契约(即接口中的抽象方法),具体实现见LoadingCache的具体实现类。
  • RemovalListener 监听器接口,在缓存被移除的时候用来做一些操作,与下面的RemovalNotification、RemovalCause配套使用。很明显这是个观察者模式的应用。
  • Weigher 权重的接口,提供int weigh(K key, V value)抽象方法,给缓存中的Entry赋予权重信息。

抽象类:

  • AbstractCache
    本身是抽象类,实现自Cache接口,基本没做什么实际的工作,大多数方法的实现只是简单抛出UnsupportedOperationException.该抽象类提供了Cache接口的骨架,为了避免子类直接继承Cache接口时必须实现所有抽象方法,这种手法在其他地方也很常见,个人觉得都算得上是一种设计模式了。
  • AbstractLoadingCache 继承自AbstractCache并实现了LoadingCache接口,目的也是提供一个骨架,其中的某些方法提供了在get不到数据时会自动Load数据的契约。
  • CacheLoader 抽象类,最核心的方法就是load,封装load数据的操作,具体如何laod与该抽象类的具体子类有关,只需要重写laod方法,就可以在get不到数据时自动去load数据到缓存中。
  • ForwardingCache
    装饰器模式的用法,所有对缓存的操作都委托给其他的实现了Cache接口的类,该抽象类中有一个抽象方法protected abstract
    Cache<K, V>
    delegate();不难推测出来,其他的方法中均使用了该代理。即类似get(key){delegate().get(key)}
  • ForwardingLoadingCache 自行推断,不解释。

类:

  • CacheBuilder 建造者模式的应用,通过该类来组装Cache,最后调用build方法来生成Cache的实例
  • CacheBuilderSpec 用来构建CacheBuilder的实例,其中提供了一些配置参数,用这些配置的参数来通过CacheBuilder实例最终构建Cache实例。
  • CacheStats 缓存使用情况统计信息,比如命中多少次,缺失多少次等等。
  • LocalCache 本地缓存最核心的类,Cache接口实例的代理人,Cache接口提供的一些方法内部均采委托给LocalCache实例来实现,LocalCache的具体实现类似于ConcurrentHashMap,也采用了分段的方式。
  • RemovalListeners 该类的文档中说的是A collection of common removal
    listeners.感觉这个类并不是这个作用,这个类提供了一个asynchronous(RemovalListener,
    Executor)的方法,代码如下:

     public static <K, V> RemovalListener<K, V> asynchronous(
    final RemovalListener<K, V> listener, final Executor executor) {
    checkNotNull(listener);
    checkNotNull(executor);
    return new RemovalListener<K, V>() {
    @Override
    public void onRemoval(final RemovalNotification<K, V> notification) {
    executor.execute(
    new Runnable() {
    @Override
    public void run() {
    listener.onRemoval(notification);
    }
    });
    }
    };
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    看这意思是将监听器转成异步执行的,也就是在移除缓存中的数据时,用异步的方法执行onRemoval操作,在onRemoval比较耗时的时候会提升性能,不至于阻塞对缓存的其他操作。

  • RemovalNotification 封装了RemovalCause,典型的观察者模式。

枚举类:

  • RemovalCause 引起移除原因的枚举类,如显示调用invalidate方法产生的移除,或者是调用replace方法时产生的移除,或者是垃圾回收导致的移除,或者是缓存过期导致的移除,或者是超过了缓存大小限制导致的移除等等。

下面列出Cache组件的特点:
- 自动加载Entry(key-value对)到缓存中
- 当缓存超过设定的最大大小时采用LRU算法进行缓存的剔除
- 可设定缓存过期时间,基于最后一次访问或者最后一次写入缓存两种方式
- keys自动使用WeakReference进行包裹
- values自动使用WeakReference或者SoftReference进行包裹
- 当Entry从缓存中剔除时会有通知机制
- 能够对缓存的使用情况进行统计。

使用示例:

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

首先调用newBuilder静态方法产生CacheBuilder对象,然后开始装配。maximumSize(10000)表示缓存最多存放10000个键值对,超过这个数会利用LRU算法进行剔除,expireAfterWrite(10, TimeUnit.MINUTES)表示在key-value对写入缓存之后10分钟过期(过期数据不会立刻进行清除,而是在下一次进行get操作的时候判断是否过期,若过期则剔除)。removalListener(MY_LISTENER)添加监听器,在进行剔除操作的时候会调用该监听器的onRemoval方法进行操作。MY_LISTENER代表实现了RemovalListener接口的子类对象。build()方法可以传入一个CacheLoader类的子类对象,该对象用来在get数据失败时进行数据的load操作,load操作的过程就在重写的load方法中。

注:如果不需要自动装载数据的功能,可以在最后的build()方法中不穿递任何参数。带不带CacheLoad类型参数的build方法代码如下所示:

  public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
checkWeightWithWeigher();
checkNonLoadingCache();
return new LocalCache.LocalManualCache<K1, V1>(this);
} public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build(
CacheLoader<? super K1, V1> loader) {
checkWeightWithWeigher();
return new LocalCache.LocalLoadingCache<K1, V1>(this, loader);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看出来它们返回的Cache实例类型分别是LocalCache.LocalManualCache和LocalCache.LocalLoadingCache这两个静态类。再去LocalCache类里面稍微看一下这两个静态内部类的继承层次。

static class LocalManualCache<K, V> implements Cache<K, V>, Serializable

static class LocalLoadingCache<K, V> extends LocalManualCache<K, V>
implements LoadingCache<K, V>
  • 1
  • 2
  • 3
  • 4
  • 5

可以看到LocalLoadingCache在继承自LocalManualCache的基础上还实现了LoadingCache接口。也就是说该类的一些方法中涉及到自动加载数据到缓存中的功能。因此构建哪种Cache完全取决于自己的需求。

最后需要提出的是,还可以重写CacheLoader中的loadAll(Iterable keys)方法,该方法可以用来批量加载数据,在哪种场景下需要这个方法呢?举一个例子,比如根据key获取value的操作需要经过网络连接,比较耗时,则批量导入数据则可以大大节省时间,也就是发一次网络请求获取一批数据回来。如果重写了loadAll,需要利用批量加载数据,那么就需要相应地调用Cache实例的getAll(Iterable keys)方法进行数据的批量获取,批量获取的过程中,若有一批key对应的value没有在缓存中,则会调用该loadAll方法进行批量加载。若没有重写loadAll方法,则会依次调用load方法去进行加载,因此是否需要重写loadAll方法可以看是否批量加载能大大节省时间。
使用示例如下:

LoadingCache<Key, Graph> loadingCache= CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
public Graph loadAll(Iterable<Key> keys) {
return createExpensiveGraphs(keys);
}
});} //用法:
Map<Key, Graph> mygraphs = loadingCache.getAll(keys);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

若需要的数据不在缓存中或者已过期,如果重写了loadAll方法,则getAll方法内部会去调用loadAll方法加载需要的数据到缓存中,如果没有重写loadAll方法,getAll内部会依次调用load方法进行数据的加载。见如下代码:

try {
Map<K, V> newEntries = loadAll(keysToLoad, defaultLoader);//该laodAll方法会在defaultLoader.loadAll()方法没有进行重写时抛出异常,被下面的catch捕获。(因为默认的loadAll重写的逻辑就是是简单地抛出一个异常。)
for (K key : keysToLoad) {
V value = newEntries.get(key);
if (value == null) {
throw new InvalidCacheLoadException("loadAll failed to return a value for " + key);
}
result.put(key, value);
}
} catch (UnsupportedLoadingOperationException e) {
// loadAll not implemented, fallback to load
for (K key : keysToLoad) {
misses--; // get will count this miss
result.put(key, get(key, defaultLoader));//捕获到异常说明没有重写loadAll方法,则在get方法中会依次调用defaultLoader的load方法进行载入
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

未完待续。。。。
如有不当之处还望指正!

Guava Cache 本地缓存组件浅析的更多相关文章

  1. Guava Cache本地缓存

    Guava介绍 Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库. 这个库是为了方便编码,并减少编码错误. 这个库提供用于集合,缓存,支持原语,并发性,常见注解,字 ...

  2. Caffeine Cache-高性能Java本地缓存组件

    前面刚说到Guava Cache,他的优点是封装了get,put操作:提供线程安全的缓存操作:提供过期策略:提供回收策略:缓存监控.当缓存的数据超过最大值时,使用LRU算法替换.这一篇我们将要谈到一个 ...

  3. Guava Cache,Java本地内存缓存使用实践

    Guava Cache,网上介绍很多,我就不赘述了. 分享一篇好的文章: Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装 Google Guava 3-缓存 在原作者基础上,我做了 ...

  4. 本地缓存之GUAVA

    项目开发中,很多配置数据需要缓存,一般来说,开发人员都会手动写HashMap,HashSet或者ConcurrentHashMap,ConcurrentHashSet缓存数据,但是这样的缓存往往存在内 ...

  5. Guava Cache源码浅析

    1. 简介 Guava Cache是指在JVM的内存中缓存数据,相比较于传统的数据库或redis存储,访问内存中的数据会更加高效,无网络开销. 根据Guava官网介绍,下面的这几种情况可以考虑使用Gu ...

  6. springboot之本地缓存(guava与caffeine)

    1. 场景描述 因项目要使用本地缓存,具体为啥不用redis等,就不讨论,记录下过程,希望能帮到需要的朋友. 2.解决方案 2.1 使用google的guava作为本地缓存 初步的想法是使用googl ...

  7. 本地缓存google.guava及分布式缓存redis 随笔

    近期项目用到了缓存,我选用的是主流的google.guava作本地缓存,redis作分布式 缓存,先说说我对本地缓存和分布式缓存的理解吧,可能不太成熟的地方,大家指出,一起 学习.本地缓存的特点是速度 ...

  8. Guava的两种本地缓存策略

    Guava的两种缓存策略 缓存在很多场景下都需要使用,如果电商网站的商品类别的查询,订单查询,用户基本信息的查询等等,针对这种读多写少的业务,都可以考虑使用到缓存.在一般的缓存系统中,除了分布式缓存, ...

  9. 同时使用Redis缓存和Google Guava本地缓存注意事项(深拷贝和浅拷贝)

    目录 1.问题场景及说明 2.Redis 缓存是深拷贝 3.Guava本地缓存直接获取则是浅拷贝 4.如何实现Guava获取本地缓存是深拷贝? 1.问题场景及说明 系统中同时使用 Redis 缓存和 ...

随机推荐

  1. yum出现Loaded plugins: fastestmirror, security Loading mirror speeds from cached hostfile解决方法

    yum出现Could not retrieve mirrorlist解决方法 Loaded plugins: fastestmirror, securityLoading mirror speeds ...

  2. 关于Http协议,你必须要知道的

    转自:https://segmentfault.com/a/1190000016751071 引言 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用 ...

  3. Python爬虫(2):urllib库

    爬虫常用库urllib 注:运行环境为PyCharm urllib是Python3内置的HTTP请求库 urllib.request:请求模块 urllib.error:异常处理模块 urllib.p ...

  4. NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇

    前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型.     (图片 ...

  5. C/C++数据在内存中的存储方式

    目录 1 内存地址 2 内存空间   在学习C/C++编程语言时,免不了和内存打交道,在计算机中,我们存储有电影,文档,音乐等数据,这些数据在内存中是以什么形式存储的呢?下面做一下简单介绍. 本文是学 ...

  6. jquery监听video标签视频播放暂停状态

    由于jquery中没有video的paly,pause方法,所以在使用jquery来控制视频的播放的播放状态时会出现问题 之前的代码: let $video = $('#video'); $('.pl ...

  7. C# int数据类型呵呵

    int16=short; int32=int; int64=long; Int16 值类型表示值介于 -32768 到 +32767 之间的有符号整数. Int32 值类型表示值介于 -2,147,4 ...

  8. asp.net core 系列 20 EF基于数据模型创建数据库

    一.概述 本章使用 Entity Framework Core 构建执行基本数据访问的 ASP.NET Core MVC 应用程序.使用迁移(migrations)基于数据模型创建数据库,是一种cod ...

  9. EF架构~FluentValidation实体检验与实体分离了

    回到目录 在MVC,EF,LINQ环境里,我们经常会用到DataModel(DO)和ViewModel(VO),可能对于它们的属性校验我们会采用特性的方式,当然这很直观,就连微软的DEMO也是如些,一 ...

  10. 【Python3爬虫】selenium入门

    selenium 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Fire ...