Java內(nèi)存緩存工具Guava LoadingCache使用解析
這篇文章主要介紹了Java內(nèi)存緩存工具Guava LoadingCache使用解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
一、Guava介紹
Guava是Google guava中的一個(gè)內(nèi)存緩存模塊,用于將數(shù)據(jù)緩存到JVM內(nèi)存中。實(shí)際項(xiàng)目開發(fā)中經(jīng)常將一些公共或者常用的數(shù)據(jù)緩存起來方便快速訪問。
Guava Cache是單個(gè)應(yīng)用運(yùn)行時(shí)的本地緩存。它不把數(shù)據(jù)存放到文件或外部服務(wù)器。如果不符合需求,可以選擇Memcached、Redis等工具。
二、代碼示例
1. POM引入
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.1-jre</version> </dependency>
2. 封裝工具類
package com.soyoung.ad.engine.util; import com.google.common.cache.*; import lombok.extern.slf4j.Slf4j; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 功能描述 * * @author 馬振全 2020/1/13 16:18 */ @Slf4j public class CacheManager { /** 緩存項(xiàng)最大數(shù)量 */ private static final long GUAVA_CACHE_SIZE = 100000; /** 緩存時(shí)間:天 */ private static final long GUAVA_CACHE_DAY = 10; /** 緩存操作對象 */ private static LoadingCache<Long, String> GLOBAL_CACHE = null; static { try { GLOBAL_CACHE = loadCache(new CacheLoader<Long, String>() { @Override public String load(Long key) throws Exception { // 處理緩存鍵不存在緩存值時(shí)的處理邏輯 return ""; } }); } catch (Exception e) { log.error("初始化Guava Cache出錯(cuò)", e); } } /** * 全局緩存設(shè)置 * * 緩存項(xiàng)最大數(shù)量:100000 * 緩存有效時(shí)間(天):10 * * * @param cacheLoader * @return * @throws Exception */ private static LoadingCache<Long, String> loadCache(CacheLoader<Long, String> cacheLoader) throws Exception { LoadingCache<Long, String> cache = CacheBuilder.newBuilder() //緩存池大小,在緩存項(xiàng)接近該大小時(shí), Guava開始回收舊的緩存項(xiàng) .maximumSize(GUAVA_CACHE_SIZE) //設(shè)置時(shí)間對象沒有被讀/寫訪問則對象從內(nèi)存中刪除(在另外的線程里面不定期維護(hù)) .expireAfterAccess(GUAVA_CACHE_DAY, TimeUnit.DAYS) // 設(shè)置緩存在寫入之后 設(shè)定時(shí)間 后失效 .expireAfterWrite(GUAVA_CACHE_DAY, TimeUnit.DAYS) //移除監(jiān)聽器,緩存項(xiàng)被移除時(shí)會(huì)觸發(fā) .removalListener(new RemovalListener<Long, String>() { @Override public void onRemoval(RemovalNotification<Long, String> rn) { //邏輯操作 } }) //開啟Guava Cache的統(tǒng)計(jì)功能 .recordStats() .build(cacheLoader); return cache; } /** * 設(shè)置緩存值 * 注: 若已有該key值,則會(huì)先移除(會(huì)觸發(fā)removalListener移除監(jiān)聽器),再添加 * * @param key * @param value */ public static void put(Long key, String value) { try { GLOBAL_CACHE.put(key, value); } catch (Exception e) { log.error("設(shè)置緩存值出錯(cuò)", e); } } /** * 批量設(shè)置緩存值 * * @param map */ public static void putAll(Map<? extends Long, ? extends String> map) { try { GLOBAL_CACHE.putAll(map); } catch (Exception e) { log.error("批量設(shè)置緩存值出錯(cuò)", e); } } /** * 獲取緩存值 * 注:如果鍵不存在值,將調(diào)用CacheLoader的load方法加載新值到該鍵中 * * @param key * @return */ public static String get(Long key) { String token = ""; try { token = GLOBAL_CACHE.get(key); } catch (Exception e) { log.error("獲取緩存值出錯(cuò)", e); } return token; } /** * 移除緩存 * * @param key */ public static void remove(Long key) { try { GLOBAL_CACHE.invalidate(key); } catch (Exception e) { log.error("移除緩存出錯(cuò)", e); } } /** * 批量移除緩存 * * @param keys */ public static void removeAll(Iterable<Long> keys) { try { GLOBAL_CACHE.invalidateAll(keys); } catch (Exception e) { log.error("批量移除緩存出錯(cuò)", e); } } /** * 清空所有緩存 */ public static void removeAll() { try { GLOBAL_CACHE.invalidateAll(); } catch (Exception e) { log.error("清空所有緩存出錯(cuò)", e); } } /** * 獲取緩存項(xiàng)數(shù)量 * * @return */ public static long size() { long size = 0; try { size = GLOBAL_CACHE.size(); } catch (Exception e) { log.error("獲取緩存項(xiàng)數(shù)量出錯(cuò)", e); } return size; } }
三、使用總結(jié)
1. 移除機(jī)制
guava做cache時(shí)候數(shù)據(jù)的移除分為被動(dòng)移除和主動(dòng)移除兩種。
被動(dòng)移除分為三種:
基于大小的移除:數(shù)量達(dá)到指定大小,會(huì)把不常用的鍵值移除
基于時(shí)間的移除:expireAfterAccess(long, TimeUnit) 根據(jù)某個(gè)鍵值對最后一次訪問之后多少時(shí)間后移除
expireAfterWrite(long, TimeUnit) 根據(jù)某個(gè)鍵值對被創(chuàng)建或值被替換后多少時(shí)間移除
基于引用的移除:主要是基于java的垃圾回收機(jī)制,根據(jù)鍵或者值的引用關(guān)系決定移除
主動(dòng)移除分為三種:1).單獨(dú)移除:Cache.invalidate(key)
2).批量移除:Cache.invalidateAll(keys)
3).移除所有:Cache.invalidateAll()
如果配置了移除監(jiān)聽器RemovalListener,則在所有移除的動(dòng)作時(shí)會(huì)同步執(zhí)行該listener下的邏輯。
如需改成異步,使用:RemovalListeners.asynchronous(RemovalListener, Executor)
2. 遇到的問題
在put操作之前,如果已經(jīng)有該鍵值,會(huì)先觸發(fā)removalListener移除監(jiān)聽器,再添加
配置了expireAfterAccess和expireAfterWrite,但在指定時(shí)間后沒有被移除。
解決方案:CacheBuilder構(gòu)建的緩存不會(huì)在特定時(shí)間自動(dòng)執(zhí)行清理和回收工作,也不會(huì)在某個(gè)緩存項(xiàng)過期后馬上清理,它不會(huì)啟動(dòng)一個(gè)線程來進(jìn)行緩存維護(hù),因?yàn)閍)線程相對較重,b)某些環(huán)境限制線程的創(chuàng)建。它會(huì)在寫操作時(shí)順帶做少量的維護(hù)工作,或者偶爾在讀操作時(shí)做。當(dāng)然,也可以創(chuàng)建自己的維護(hù)線程,以固定的時(shí)間間隔調(diào)用Cache.cleanUp()。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java+opencv3.2.0實(shí)現(xiàn)重映射
這篇文章主要為大家詳細(xì)介紹了Java+opencv3.2.0實(shí)現(xiàn)重映射的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02JAVA 時(shí)間區(qū)間的字符串合法性驗(yàn)證
需要對獲得的諸如08:30-11:00這樣的字符串進(jìn)行合法性驗(yàn)證,判定表示的時(shí)間區(qū)間是否合法,以及對高峰期時(shí)間的區(qū)間是否在總的時(shí)間區(qū)間內(nèi)部進(jìn)行判斷。2013-03-03JAVA實(shí)現(xiàn) springMVC方式的微信接入、實(shí)現(xiàn)消息自動(dòng)回復(fù)實(shí)例
本篇文章主要介紹了JAVA實(shí)現(xiàn) springMVC方式的微信接入、實(shí)現(xiàn)消息自動(dòng)回復(fù),這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2016-12-12mybatis主表與明細(xì)表一對多的同時(shí)插入操作方法
對主表(采購申請表)和明細(xì)表(申請物資表)同時(shí)進(jìn)行插入操作insert,怎么實(shí)現(xiàn)呢,下面給大家分享mybatis主表與明細(xì)表一對多的同時(shí)插入操作方法,感興趣的朋友一起看看吧2023-02-02使用MDC快速查詢應(yīng)用接口全部執(zhí)行日志
這篇文章主要為大家介紹了使用MDC快速查詢應(yīng)用接口全部執(zhí)行日志的方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01SpringBoot 錯(cuò)誤處理機(jī)制與自定義錯(cuò)誤處理實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringBoot 錯(cuò)誤處理機(jī)制與自定義錯(cuò)誤處理實(shí)現(xiàn)詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11SpringCloud超詳細(xì)講解Feign聲明式服務(wù)調(diào)用
Feign可以把Rest的請求進(jìn)行隱藏,偽裝成類似Spring?MVC的Controller一樣。不用再自己拼接url,拼接參數(shù)等等操作,一切都交給Feign去做2022-06-06