guava、caffeine、ohc(堆外缓存)详解
一、Guava缓存
Guava Cache适用于以下场景:
- 你愿意消耗一些内存空间来提升速度。
- 你预料到某些键会被查询一次以上。
- 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Redis这类工具)
仓库坐标如下:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
代码详细示例:
@Data
public class CacheVO {
private String name;
public CacheVO(String name) {
this.name = name;
}
}
public class GuavaCacheMangerService {
private static LoadingCache<String, CacheVO> cache;
private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new
LinkedBlockingQueue<Runnable>(1204));
static {
cache = CacheBuilder.newBuilder()
// 缓存项在给定时间内没有被读/写访问,则回收。
.expireAfterAccess(500, TimeUnit.SECONDS)
// 缓存项在给定时间内没有被写访问(创建或覆盖),则回收。
// 如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
.expireAfterWrite(500, TimeUnit.SECONDS)
// 初始化容量大小
.initialCapacity(1024 * 100)
// 缓存项的数目不超过固定值
.maximumSize(1024 * 100)
// 可以为缓存增加自动刷新功能,配合CacheLoader reload使用
.refreshAfterWrite(1, TimeUnit.SECONDS)
// 加载缓存
.build(new CacheLoader<String, CacheVO>() {
// 单个加载,要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值。
@Override
public CacheVO load(String s) throws Exception {
return createCacheVO(s);
}
// 批量加载,对每个不在缓存的键,getAll方法会单独调用CacheLoader.load来加载缓存项。
// 如果批量加载比多个单独加载更高效,你可以重载CacheLoader.loadAll来利用这一点。
@Override
public Map<String, CacheVO> loadAll(Iterable<? extends String> keys) throws Exception {
return createBatchCacheVOs(keys);
}
// 异步刷新加载新值,在刷新操作进行时,缓存仍然可以向其他线程返回旧值,
// 而不像回收操作,读缓存的线程必须等待新值加载完成。
@Override
public ListenableFuture<CacheVO> reload(String key, CacheVO oldValue) throws Exception {
if (needRefresh()) {
return Futures.immediateFuture(oldValue);
}
ListenableFutureTask<CacheVO> task = ListenableFutureTask.create(() -> {return createCacheVO(key);});
executorService.execute(task);
return task;
}
});
}
public static boolean needRefresh() {
Random ra =new Random();
return (ra.nextInt(10) % 2) > 0 ? true : false;
}
public static CacheVO createCacheVO(String key){
return new CacheVO(key);
}
public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {
Map<String, CacheVO> result = new HashMap<>();
for (String key : keys) {
result.put(key, new CacheVO(key));
}
return result;
}
public static void main(String[] args) throws Exception{
// 单个获取
CacheVO cacheVO1 = cache.get("AA");
// 如果有缓存则返回;否则运算、缓存、然后返回,整个过程是阻塞的
// 在整个加载方法完成前,缓存项相关的可观察状态都不会更改。
CacheVO cacheVO2 = cache.get("BB", () -> {return createCacheVO("BB");});
List<String> list = new ArrayList<>();
list.add("CC");
list.add("DD");
// 批量获取
Map<String, CacheVO> cacheMap = cache.getAll(list);
// 个别清除
cache.invalidate("AA");
// 批量清除
cache.invalidateAll(list);
// 清除所有
cache.invalidateAll();
}
}
二、Caffeine缓存
Caffeine是一种高性能的缓存库,是基于Java 8的最佳(最优)缓存框架,性能各方面优于guava。
代码仓库如下:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.4.0</version>
</dependency>
代码详细示例如下:
public class CaffeineCacheMangerService {
private static LoadingCache<String, CacheVO> cache;
private static AsyncLoadingCache<String, CacheVO> asyncCache;
private static AsyncLoadingCache<String, CacheVO> asyncCache1;
private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new
LinkedBlockingQueue<Runnable>(1204));
static {
cache = Caffeine.newBuilder()
// 初始化缓存长度
.initialCapacity(1024 * 10)
// 最大长度
.maximumSize(1024 * 10)
// 更新策略
.refreshAfterWrite(10, TimeUnit.SECONDS)
// 设置缓存的过期时间
.expireAfterWrite(10, TimeUnit.SECONDS)
.build(new CacheLoader<String, CacheVO>() {
// 同步加载
@CheckForNull
@Override
public CacheVO load(@Nonnull String key) throws Exception {
return createCacheVO(key);
}
// getAll将会对缓存中没有值的key分别调用CacheLoader.load方法来构建缓存的值。
// 我们可以重写CacheLoader.loadAll方法来提高getAll的效率。
@Nonnull
@Override
public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) throws Exception {
return createBatchCacheVOs(keys);
}
});
// 异步加载 同步load写法,最后也会转异步
asyncCache = Caffeine.newBuilder()
.maximumSize(1024 * 10)
.expireAfterWrite(10, TimeUnit.SECONDS)
.buildAsync(new CacheLoader<String, CacheVO>() {
@CheckForNull
@Override
public CacheVO load(@Nonnull String key) throws Exception {
return createCacheVO(key);
}
@Nonnull
@Override
public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) {
return createBatchCacheVOs(keys);
}
});
// 异步加载 异步load写法
asyncCache1 = Caffeine.newBuilder()
.maximumSize(1024 * 10)
.expireAfterWrite(10, TimeUnit.SECONDS)
.buildAsync(new AsyncCacheLoader<String, CacheVO>() {
@Nonnull
@Override
public CompletableFuture<CacheVO> asyncLoad(@Nonnull String key, @Nonnull Executor executor) {
return asyncCreateCacheVO(key, executor);
}
@Nonnull
@Override
public CompletableFuture<Map<String, CacheVO>> asyncLoadAll(@Nonnull Iterable<? extends String> keys, @Nonnull Executor executor) {
return asyncCreateBatchCacheVOs(keys, executor);
}
});
}
public static CompletableFuture<CacheVO> asyncCreateCacheVO(String key, Executor executor) {
return CompletableFuture.supplyAsync(() -> createCacheVO(key), executor);
}
public static CompletableFuture<Map<String, CacheVO>> asyncCreateBatchCacheVOs(Iterable<? extends String> keys, Executor executor) {
return CompletableFuture.supplyAsync(() -> createBatchCacheVOs(keys),executor);
}
public static CacheVO createCacheVO(String key) {
return new CacheVO(key);
}
public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {
Map<String, CacheVO> result = new HashMap<>();
for (String key : keys) {
result.put(key, new CacheVO(key));
}
return result;
}
public static void main(String[] args) throws Exception {
CacheVO cacheVO1 = cache.get("AA");
List<String> list = new ArrayList<>();
list.add("BB");
list.add("CC");
Map<String, CacheVO> map = cache.getAll(list);
// 如果有缓存则返回;否则运算、缓存、然后返回,整个过程是阻塞的
// 即使多个线程同时请求该值也只会调用一次Function方法