如何基于LoadingCache實(shí)現(xiàn)Java本地緩存
這篇文章主要介紹了如何基于LoadingCache實(shí)現(xiàn)Java本地緩存,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
前言
Guava是Google開源出來的一套工具庫。其中提供的cache模塊非常方便,是一種與ConcurrentMap相似的緩存Map。
官方地址:https://github.com/google/guava/wiki/CachesExplained
開始構(gòu)建
一. 添加依賴
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.1-jre</version> </dependency>
二.創(chuàng)建 CacheLoader
LoadingCache<Long, String> cache = CacheBuilder.newBuilder() //緩存池大小,在緩存項(xiàng)接近該大小時, Guava開始回收舊的緩存項(xiàng) .maximumSize(GUAVA_CACHE_SIZE) //設(shè)置時間對象沒有被讀/寫訪問則對象從內(nèi)存中刪除(在另外的線程里面不定期維護(hù)) .expireAfterAccess(10, TimeUnit.MINUTES) //移除監(jiān)聽器,緩存項(xiàng)被移除時會觸發(fā) .removalListener(new RemovalListener <Long, String>() { @Override public void onRemoval(RemovalNotification<Long, String> rn) { //執(zhí)行邏輯操作 } }) //開啟Guava Cache的統(tǒng)計(jì)功能 .recordStats() .build(cacheLoader);
三.個人封裝的工具類
package com.xxx; import com.google.common.cache.*; import org.slf4j.Logger; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; public class CacheManager { private static Logger log = Log.get(); /** 緩存項(xiàng)最大數(shù)量 */ private static final long GUAVA_CACHE_SIZE = 100000; /** 緩存時間:天 */ 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 { // 處理緩存鍵不存在緩存值時的處理邏輯 return ""; } }); } catch (Exception e) { log.error("初始化Guava Cache出錯", e); } } /** * 全局緩存設(shè)置 * * 緩存項(xiàng)最大數(shù)量:100000 * 緩存有效時間(天):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)接近該大小時, Guava開始回收舊的緩存項(xiàng) .maximumSize(GUAVA_CACHE_SIZE) //設(shè)置時間對象沒有被讀/寫訪問則對象從內(nèi)存中刪除(在另外的線程里面不定期維護(hù)) .expireAfterAccess(GUAVA_CACHE_DAY, TimeUnit.DAYS) // 設(shè)置緩存在寫入之后 設(shè)定時間 后失效 .expireAfterWrite(GUAVA_CACHE_DAY, TimeUnit.DAYS) //移除監(jiān)聽器,緩存項(xiàng)被移除時會觸發(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值,則會先移除(會觸發(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è)置緩存值出錯", 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è)置緩存值出錯", 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("獲取緩存值出錯", e); } return token; } /** * 移除緩存 * * @param key */ public static void remove(Long key) { try { GLOBAL_CACHE.invalidate(key); } catch (Exception e) { log.error("移除緩存出錯", e); } } /** * 批量移除緩存 * * @param keys */ public static void removeAll(Iterable<Long> keys) { try { GLOBAL_CACHE.invalidateAll(keys); } catch (Exception e) { log.error("批量移除緩存出錯", e); } } /** * 清空所有緩存 */ public static void removeAll() { try { GLOBAL_CACHE.invalidateAll(); } catch (Exception e) { log.error("清空所有緩存出錯", 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ù)量出錯", e); } return size; } }
總結(jié)
1.移除機(jī)制
guava做cache時候數(shù)據(jù)的移除分為被動移除和主動移除兩種。
被動移除分為三種:1).基于大小的移除:數(shù)量達(dá)到指定大小,會把不常用的鍵值移除
2).基于時間的移除:expireAfterAccess(long, TimeUnit) 根據(jù)某個鍵值對最后一次訪問之后多少時間后移除
expireAfterWrite(long, TimeUnit) 根據(jù)某個鍵值對被創(chuàng)建或值被替換后多少時間移除
3).基于引用的移除:主要是基于java的垃圾回收機(jī)制,根據(jù)鍵或者值的引用關(guān)系決定移除
主動移除分為三種:1).單獨(dú)移除:Cache.invalidate(key)
2).批量移除:Cache.invalidateAll(keys)
3).移除所有:Cache.invalidateAll()
如果配置了移除監(jiān)聽器RemovalListener,則在所有移除的動作時會同步執(zhí)行該listener下的邏輯。
如需改成異步,使用:RemovalListeners.asynchronous(RemovalListener, Executor)
2.遇到的問題
1). 在put操作之前,如果已經(jīng)有該鍵值,會先觸發(fā)removalListener移除監(jiān)聽器,再添加
2). 配置了expireAfterAccess和expireAfterWrite,但在指定時間后沒有被移除。
解決方案:CacheBuilder在文檔上有說明
If expireAfterWrite or expireAfterAccess is requested entries may be evicted on each cache modification, on occasional cache accesses, or on calls to Cache.cleanUp(). Expired entries may be counted in Cache.size(), but will never be visible to read or write operations.
翻譯過來大概的意思是:CacheBuilder構(gòu)建的緩存不會在特定時間自動執(zhí)行清理和回收工作,也不會在某個緩存項(xiàng)過期后馬上清理,它不會啟動一個線程來進(jìn)行緩存維護(hù),因?yàn)?/p>
a)線程相對較重
b)某些環(huán)境限制線程的創(chuàng)建。它會在寫操作時順帶做少量的維護(hù)工作,或者偶爾在讀操作時做
當(dāng)然,也可以創(chuàng)建自己的維護(hù)線程,以固定的時間間隔調(diào)用Cache.cleanUp()。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:Java內(nèi)存模型原子性原理及實(shí)例解析
欄 目:Java
下一篇:淺析Java 9 Optional API 新增方法
本文標(biāo)題:如何基于LoadingCache實(shí)現(xiàn)Java本地緩存
本文地址:http://mengdiqiu.com.cn/a1/Java/8905.html
您可能感興趣的文章
- 01-10JavaWeb實(shí)現(xiàn)郵件發(fā)送功能
- 01-10java基于poi導(dǎo)出excel透視表代碼實(shí)例
- 01-10基于Java驗(yàn)證jwt token代碼實(shí)例
- 01-10如何解決線程太多導(dǎo)致java socket連接池出現(xiàn)的問題
- 01-10如何解決java壓縮文件亂碼問題
- 01-10如何基于SpringBoot部署外部Tomcat過程解析
- 01-10如何基于Spring使用工廠模式實(shí)現(xiàn)程序解耦
- 01-10如何使用Spring工具類動態(tài)匹配url
- 01-10基于SPRINGBOOT配置文件占位符過程解析
- 01-10SpringBoot基于數(shù)據(jù)庫實(shí)現(xiàn)定時任務(wù)過程解析


閱讀排行
本欄相關(guān)
- 01-10Java實(shí)現(xiàn)動態(tài)模擬時鐘
- 01-10Springboot中@Value的使用詳解
- 01-10JavaWeb實(shí)現(xiàn)郵件發(fā)送功能
- 01-10利用Java實(shí)現(xiàn)復(fù)制Excel工作表功能
- 01-10Java實(shí)現(xiàn)動態(tài)數(shù)字時鐘
- 01-10java基于poi導(dǎo)出excel透視表代碼實(shí)例
- 01-10java實(shí)現(xiàn)液晶數(shù)字字體顯示當(dāng)前時間
- 01-10基于Java驗(yàn)證jwt token代碼實(shí)例
- 01-10Java動態(tài)顯示當(dāng)前日期和時間
- 01-10淺談Java中真的只有值傳遞么
隨機(jī)閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法