我是靠谱客的博主 甜美豆芽,最近开发中收集的这篇文章主要介绍Guava 缓存教程Guava 缓存,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Guava 缓存

本文我们看下Guava Cache的实现,包括基本使用,驱逐策略,刷新缓存以及一些有趣的批处理操作。最后,我们再看看缓存发出的删除通知功能。

如何使用Guava Cache

先来看一个简单示例,缓存字符串实例的大小形式。首先,我们创建ChcheLoader,用于计算存储在缓存中的值,然后我们便捷的CacheBuilder类依照规范构建缓存:

@Test
public void whenCacheMiss_thenValueIsComputed() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder().build(loader);
assertEquals(0, cache.size());
assertEquals("HELLO", cache.getUnchecked("hello"));
assertEquals(1, cache.size());
}

因为“hello” 键对应值在缓存中没有,所以值被计算并缓存。注意,我们使用getUnchecked() 方法,如果对应值不存在,则计算并缓存值到缓存中。

驱逐策略

每个缓存需要在必要时删除内容,让我们讨论下从缓存中清除值机制——使用不同的标准。

按大小驱逐

我们能通过 maximumSize()方法限制缓存大小,如果缓存达到上限,最老的项将被驱逐。
下面示例代码中,我们限制缓存大小为3条记录:

@Test
public void whenCacheReachMaxSize_thenEviction() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder().maximumSize(3).build(loader);
cache.getUnchecked("first");
cache.getUnchecked("second");
cache.getUnchecked("third");
cache.getUnchecked("forth");
assertEquals(3, cache.size());
assertNull(cache.getIfPresent("first"));
assertEquals("FORTH", cache.getIfPresent("forth"));
}

按权重驱逐

我们也可以按照自定义权重功能限制缓存大小,下面代码,使用length作为我们自定义权重函数:

@Test
public void whenCacheReachMaxWeight_thenEviction() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
Weigher<String, String> weighByLength;
weighByLength = new Weigher<String, String>() {
@Override
public int weigh(String key, String value) {
return value.length();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder()
.maximumWeight(16)
.weigher(weighByLength)
.build(loader);
cache.getUnchecked("first");
cache.getUnchecked("second");
cache.getUnchecked("third");
cache.getUnchecked("last");
assertEquals(3, cache.size());
assertNull(cache.getIfPresent("first"));
assertEquals("LAST", cache.getIfPresent("last"));
}

注意,缓存可能删除多条记录,为较长记录预留空间。

按时间驱逐

除了按大小驱逐较早记录,我们还可以采用时间方式。下面示例我们自定义缓存删除已经闲置2ms的记录:

@Test
public void whenEntryIdle_thenEviction()
throws InterruptedException {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder()
.expireAfterAccess(2,TimeUnit.MILLISECONDS)
.build(loader);
cache.getUnchecked("hello");
assertEquals(1, cache.size());
cache.getUnchecked("hello");
Thread.sleep(300);
cache.getUnchecked("test");
assertEquals(1, cache.size());
assertNull(cache.getIfPresent("hello"));
}

我们也可以基于整个生命时间来驱逐,下面示例中,被存储2ms后记录被清除缓存。

@Test
public void whenEntryLiveTimeExpire_thenEviction()
throws InterruptedException {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder()
.expireAfterWrite(2,TimeUnit.MILLISECONDS)
.build(loader);
cache.getUnchecked("hello");
assertEquals(1, cache.size());
Thread.sleep(300);
cache.getUnchecked("test");
assertEquals(1, cache.size());
assertNull(cache.getIfPresent("hello"));
}

弱引用键(Weak key)

接下来,看看如何使缓存键为弱引用,即允许垃圾收集器收集在其他地方没有引用的缓存键。缺省缓存键和值都为强引用,但我们能使用weakKeys()方法使缓存键为弱引用,示例如下:

@Test
public void whenWeakKeyHasNoRef_thenRemoveFromCache() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder().weakKeys().build(loader);
}

缓存值软引用(soft valuel)

我们可以使用softValues()方法让垃圾回收器收集缓存值,防止内存溢出:

@Test
public void whenSoftValue_thenRemoveFromCache() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder().softValues().build(loader);
}

需要注意的时,软引用太多会影响系统性能,最好使用maximumSize()方法。

处理null值

现在,让我们看看缓存如何null值。默认情况下,Guava Cache如果加载null值会抛出异常,因为缓存null值没有意义。
但如果null值在你代码中有意义,那你可以使用Optional类实现:

@Test
public void whenNullValue_thenOptional() {
CacheLoader<String, Optional<String>> loader;
loader = new CacheLoader<String, Optional<String>>() {
@Override
public Optional<String> load(String key) {
return Optional.fromNullable(getSuffix(key));
}
};
LoadingCache<String, Optional<String>> cache;
cache = CacheBuilder.newBuilder().build(loader);
assertEquals("txt", cache.getUnchecked("text.txt").get());
assertFalse(cache.getUnchecked("hello").isPresent());
}
private String getSuffix(final String str) {
int lastIndex = str.lastIndexOf('.');
if (lastIndex == -1) {
return null;
}
return str.substring(lastIndex + 1);
}

刷新缓存

下面看看如何刷新缓存值,可以使用refreshAfterWrite()方法自动刷新。下面示例中,每分钟自动刷新:

@Test
public void whenLiveTimeEnd_thenRefresh() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder()
.refreshAfterWrite(1,TimeUnit.MINUTES)
.build(loader);
}

也可以手动通过refresh(key)方法刷新缓存中指定记录。

预缓存

我们可以通过putAll()方法在缓存中插入多条记录。下面示例中,通过Map往缓存中增加多条记录:

@Test
public void whenPreloadCache_thenUsePutAll() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder().build(loader);
Map<String, String> map = new HashMap<String, String>();
map.put("first", "FIRST");
map.put("second", "SECOND");
cache.putAll(map);
assertEquals(2, cache.size());
}

删除通知

有时当缓存记录删除时,需要处理一些业务,下面讨论下RemovalNotification。
我们可以注册RemovalListener 监听器获得删除通知,还可以通过getCause()方法访问删除原因。
下面示例中,缓存中第四个元素删除时会收到通知:

@Test
public void whenEntryRemovedFromCache_thenNotify() {
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(final String key) {
return key.toUpperCase();
}
};
RemovalListener<String, String> listener;
listener = new RemovalListener<String, String>() {
@Override
public void onRemoval(RemovalNotification<String, String> n){
if (n.wasEvicted()) {
String cause = n.getCause().name();
assertEquals(RemovalCause.SIZE.toString(),cause);
}
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder()
.maximumSize(3)
.removalListener(listener)
.build(loader);
cache.getUnchecked("first");
cache.getUnchecked("second");
cache.getUnchecked("third");
cache.getUnchecked("last");
assertEquals(3, cache.size());
}

补充说明

最后,有一些关于Guava 缓存实现的补充说明:

  • 是线程安全的

  • put(key,value)方法可以手动插入记录

  • CacheStats ( hitRate(), missRate(), ..)等方法可以监控缓存性能

总结

文本通过示例说明Guava缓存主要功能及应用,从简单使用到元素驱逐,刷新缓存,预缓存以及删除通知等。

最后

以上就是甜美豆芽为你收集整理的Guava 缓存教程Guava 缓存的全部内容,希望文章能够帮你解决Guava 缓存教程Guava 缓存所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部