簡介
guava是google的一個開源java框架,其github地址是 https://github.com/google/guava。guava工程包含了若干被Google的Java項目廣泛依賴的核心庫,例如:
- 集合 [collections]
- 緩存 [caching]
- 原生類型支持 [primitives support]
- 併發庫 [concurrency libraries]
- 通用註解 [common annotations]
- 字符串處理 [string processing]
- I/O
所有這些工具每天都在被Google的工程師應用在產品服務中。 其中caching這一塊是常用的模塊的之一
引入依賴
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
使用示例
基本用法
@Component
public final class WsPlasRoleCache {
private static Logger logger = LoggerFactory.getLogger(WsPlasRoleCache.class);
private static AtomicBoolean isInited = new AtomicBoolean(false);
private static LoadingCache<String, List<WsPlasRole>> cache;
private WsPlasRoleCache() {
if (!isInited.get()) {
init();
}
}
private static void init() {
logger.info("---初始化WsPlasRole");
cache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, List<WsPlasRole>>() {
@Override
public List<WsPlasRole> load(String key) throws Exception {
// 如果是第一次,調用load方法;如果是兩次get時間在10分鐘之間,就不會調用load
PlasRoleManager service = SpringContextHolder.getBean("plasRoleManager");
logger.info("查詢WsPlasRole");
List<WsPlasRole> list = service.getWsAll(key);
return list;
}
});
isInited.set(true);
}
public static List<WsPlasRole> getWsAll(String key) {
try {
return cache.get(key);
} catch (Exception e) {
}
return null;
}
}
利用@Component讓工程啓動的時候初始化cache對象,重寫構造函數並且設置爲private是爲了保證是單例的
核心方法
- refreshAfterWrite : 寫入數據後多久過期,只阻塞當前數據加載線程,其他線程返回舊值
- expireAfterWrite : 寫緩存後多久過期
- expireAfterAccess : 讀寫緩存後多久過期
- maximumSize : 設置緩存最大容量,超過之後就會按照LRU最近雖少使用算法來移除緩存項
異步刷新
抽象類CacheLoader是同步刷新的。
如果緩存到期了,有多個線程調用緩存,那麼第一個調用的會阻塞住,其他的線程會直接返回已過期的緩存
如果想要讓所有的線程都不阻塞,就要創建異步加載類
public abstract class RefreshAsyncCacheLoader<K, V> extends CacheLoader<K, V> {
private ExecutorService threadPool = Executors.newSingleThreadExecutor();
@Override
public ListenableFuture<V> reload(final K key, final V oldValue) throws Exception {
checkNotNull(key);
checkNotNull(oldValue);
//增加配置參數,控制是否異步加載;1-異步加載,0-同步加載
String async = PropertyConfigurer.getProperty("cache.refres.async");
if (StringUtils.equals(async,"1")){
ListenableFutureTask<V> task = ListenableFutureTask.create(new Callable<V>() {
public V call() {
try {
return load((K) key);
} catch (Exception e) {
}
return oldValue;
}
});
threadPool.execute(task);
return task;
}else{
return Futures.immediateFuture(load(key));
}
}
}
將CacheLoader替換成異步實現類RefreshAsyncCacheLoader
public static void init() {
logger.info("---初始化WsPlasUserCache");
cache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES)
.build(new RefreshAsyncCacheLoader<String, List<WsPlasUser>>() {
@Override
public List<WsPlasUser> load(String key) throws Exception {
PlasUserManager service = SpringContextHolder.getBean("plasUserManager");
logger.info("查詢WsPlasUser");
List<WsPlasUser> list = service.getWsAll();
return list;
}
});
}
顯示清除
緩存會自動過期,也可以手動讓其過期,主要有以下方法:
- 個別清除:Cache.invalidate(key)
- 批量清除:Cache.invalidateAll(keys)
- 清除所有緩存項:Cache.invalidateAll()
總結
- guava cache是本地緩存,和redis、memcached等集中緩存不同,如果是集羣環境,本地緩存有不同步的問題,如果對緩存同步時間要求不是太高,這完全不是問題
- 相比於redis等集中緩存,本地緩存性能更高;因爲沒有網絡交互,直接內存讀取
- guava cache使用簡單、控制靈活