最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java基于LoadingCache實現(xiàn)本地緩存的示例代碼

 更新時間:2022年01月14日 15:29:46   作者:你哈嘍啊  
本文主要介紹了Java基于LoadingCache實現(xiàn)本地緩存的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

一、 添加 maven 依賴

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>27.1-jre</version>
</dependency>

二、CacheBuilder 方法說明

1??LoadingCache build(CacheLoader loader)

2??CacheBuilder.maximumSize(long size)配置緩存數(shù)量上限,快達到上限或達到上限,處理了時間最長沒被訪問過的對象或者根據(jù)配置的被釋放的對象

3??expireAfterAccess(long, TimeUnit)緩存項在給定時間內(nèi)沒有被讀/寫訪問,則回收。請注意這種緩存的回收順序和基于大小回收一樣

4??expireAfterWrite(long, TimeUnit)緩存項在給定時間內(nèi)沒有被寫訪問(創(chuàng)建或覆蓋),則回收。如果認為緩存數(shù)據(jù)總是在固定時候后變得陳舊不可用,這種回收方式是可取的。

5??refreshAfterWrite(long duration, TimeUnit unit)定時刷新,可以為緩存增加自動定時刷新功能。和expireAfterWrite相反,refreshAfterWrite通過定時刷新可以讓緩存項保持可用,但請注意:緩存項只有在被檢索時才會真正刷新,即只有刷新間隔時間到了再去get(key)才會重新去執(zhí)行Loading,否則就算刷新間隔時間到了也不會執(zhí)行l(wèi)oading操作。因此,如果在緩存上同時聲明expireAfterWrite和refreshAfterWrite,緩存并不會因為刷新盲目地定時重置,如果緩存項沒有被檢索,那刷新就不會真的發(fā)生,緩存項在過期時間后也變得可以回收。還有一點比較重要的是refreshAfterWrite和expireAfterWrite兩個方法設(shè)置以后,重新get會引起loading操作都是同步串行的。這其實可能會有一個隱患,當某一個時間點剛好有大量檢索過來而且都有刷新或者回收的話,是會產(chǎn)生大量的請求同步調(diào)用loading方法,這些請求占用線程資源的時間明顯變長。如正常請求也就20ms,當刷新以后加上同步請求loading這個功能接口可能響應(yīng)時間遠遠大于20ms。為了預(yù)防這種井噴現(xiàn)象,可以不設(shè)refreshAfterWrite方法,改用LoadingCache.refresh(K)因為它是異步執(zhí)行的,不會影響正在讀的請求,同時使用ScheduledExecutorService可以很好地實現(xiàn)這樣的定時調(diào)度,配上cache.asMap().keySet()返回當前所有已加載鍵,這樣所有的key定時刷新就有了。如果訪問量沒有這么大則直接用CacheBuilder.refreshAfterWrite(long, TimeUnit)也可以。

三、創(chuàng)建 CacheLoader

LoadingCache<Long, String> cache = CacheBuilder.newBuilder()
? ? ? ? //緩存池大小,在緩存項接近該大小時, Guava開始回收舊的緩存項
? ? ? ? .maximumSize(10000)
? ? ? ? //設(shè)置時間對象沒有被讀/寫訪問則對象從內(nèi)存中刪除(在另外的線程里面不定期維護)
? ? ? ? .expireAfterAccess(10, TimeUnit.MINUTES)
? ? ? ? //移除監(jiān)聽器,緩存項被移除時會觸發(fā)
? ? ? ? .removalListener(new RemovalListener <Long, String>() {
? ? ? ? ? @Override
? ? ? ? ? public void onRemoval(RemovalNotification<Long, String> rn) {
? ? ? ? ? ? //執(zhí)行邏輯操作
? ? ? ? ? }
? ? ? ? })
? ? ? ? .recordStats()//開啟Guava Cache的統(tǒng)計功能
? ? ? ? .build(new CacheLoader<String, Object>() {
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? public Object load(String key) {
? ? ? ? ? ? ? ? ? ? ? ?//從 SQL或者NoSql 獲取對象
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? });//CacheLoader類 實現(xiàn)自動加載

四、工具類

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();
? /** 緩存項最大數(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è)置
? ?* 緩存項最大數(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()
? ? ? ? //緩存池大小,在緩存項接近該大小時, Guava開始回收舊的緩存項
? ? ? ? .maximumSize(GUAVA_CACHE_SIZE)
? ? ? ? //設(shè)置時間對象沒有被讀/寫訪問則對象從內(nèi)存中刪除(在另外的線程里面不定期維護)
? ? ? ? .expireAfterAccess(GUAVA_CACHE_DAY, TimeUnit.DAYS)
? ? ? ? // 設(shè)置緩存在寫入之后 設(shè)定時間 后失效
? ? ? ? .expireAfterWrite(GUAVA_CACHE_DAY, TimeUnit.DAYS)
? ? ? ? //移除監(jiān)聽器,緩存項被移除時會觸發(fā)
? ? ? ? .removalListener(new RemovalListener <Long, String>() {
? ? ? ? ? @Override
? ? ? ? ? public void onRemoval(RemovalNotification<Long, String> rn) {
? ? ? ? ? ? //邏輯操作
? ? ? ? ? }
? ? ? ? })
? ? ? ? //開啟Guava Cache的統(tǒng)計功能
? ? ? ? .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);
? ? }
? }

? /**
? ?* 獲取緩存項數(shù)量
? ?* @return
? ?*/
? public static long size() {
? ? long size = 0;
? ? try {
? ? ? size = GLOBAL_CACHE.size();
? ? } catch (Exception e) {
? ? ? log.error("獲取緩存項數(shù)量出錯", e);
? ? }
? ? return size;
? }
}

五、guava Cache數(shù)據(jù)移除

1??移除機制

guava做cache的時候,數(shù)據(jù)的移除分為被動移除和主動移除兩種。

【被動移除分為三種】
1)基于大小的移除:
按照緩存的大小來移除,如果即將到達指定的大小,那就會把不常用的鍵值對從cache中移除。定義的方式一般為 CacheBuilder.maximumSize(long),還有一種可以算權(quán)重的方法,個人認為實際使用中不太用到。就這個常用有一下注意點:

a. 這個size指的是cache中的條目數(shù),不是內(nèi)存大小或是其他;
b. 并不是完全到了指定的size系統(tǒng)才開始移除不常用的數(shù)據(jù)的,而是接近這個size的時候系統(tǒng)就會開始做移除的動作;
c. 如果一個鍵值對已經(jīng)從緩存中被移除了,再次請求訪問的時候,如果cachebuild是使用cacheloader方式的,那依然還是會從cacheloader中再取一次值,如果這樣還沒有,就會拋出異常。

2)基于時間的移除:
expireAfterAccess(long, TimeUnit) 根據(jù)某個鍵值對最后一次訪問之后多少時間后移除;
expireAfterWrite(long, TimeUnit) 根據(jù)某個鍵值對被創(chuàng)建或值被替換后多少時間移除
3)基于引用的移除:主要是基于Java的垃圾回收機制,根據(jù)鍵或者值的引用關(guān)系決定移除

【主動移除分為三種】
1)單獨移除:Cache.invalidate(key)
2)批量移除:Cache.invalidateAll(keys)
3)移除所有:Cache.invalidateAll()

如果需要在移除數(shù)據(jù)的時候有所動作還可以定義Removal Listener,但是有點需要注意的是默認Removal Listener中的行為是和移除動作同步執(zhí)行的,如果需要改成異步形式,可以考慮使用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í)行清理和回收工作,也不會在某個緩存項過期后馬上清理,它不會啟動一個線程來進行緩存維護,因為:

a)線程相對較重
b)某些環(huán)境限制線程的創(chuàng)建。它會在寫操作時順帶做少量的維護工作,或者偶爾在讀操作時做

當然,也可以創(chuàng)建自己的維護線程,以固定的時間間隔調(diào)用Cache.cleanUp()。

到此這篇關(guān)于Java基于LoadingCache實現(xiàn)本地緩存的示例代碼的文章就介紹到這了,更多相關(guān)Java LoadingCache本地緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JPA中@CreatedDate和@LastModifiedDate的使用方式

    JPA中@CreatedDate和@LastModifiedDate的使用方式

    這篇文章主要介紹了JPA中@CreatedDate和@LastModifiedDate的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 詳解SSM框架下結(jié)合log4j、slf4j打印日志

    詳解SSM框架下結(jié)合log4j、slf4j打印日志

    本篇文章主要介紹了詳解SSM框架下結(jié)合log4j、slf4j打印日志,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • 使用lombok注解導(dǎo)致mybatis-plus TypeHandler失效的解決

    使用lombok注解導(dǎo)致mybatis-plus TypeHandler失效的解決

    這篇文章主要介紹了使用lombok注解導(dǎo)致mybatis-plus TypeHandler失效的解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • SpringBoot讀取配置的6種方式

    SpringBoot讀取配置的6種方式

    本文主要介紹了SpringBoot讀取配置的6種方式,主要包括使用默認配置、使用application.properties文件、使用application.yml文件、使用@Value注解、使用Environment對象和使用ConfigurableEnvironment對象,感興趣的可以了解一下
    2023-08-08
  • Java異常日志堆棧丟失的原因與排查

    Java異常日志堆棧丟失的原因與排查

    這篇文章主要給大家介紹了關(guān)于Java異常日志堆棧丟失的原因與排查的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Java 在PPT中添加混合圖表過程詳解

    Java 在PPT中添加混合圖表過程詳解

    這篇文章主要介紹了Java 在PPT中添加混合圖表過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • 什么是Java自旋鎖

    什么是Java自旋鎖

    這篇文章主要介紹了什么是Java自旋鎖,在有些場景中,同步資源的鎖定時間很短,為了這一小段時間去切換線程,線程掛起和恢復(fù)現(xiàn)場的花費可能會讓系統(tǒng)得不償失,下面來了解具體內(nèi)容介紹吧
    2022-01-01
  • Java工作環(huán)境的配置與Eclipse的安裝過程

    Java工作環(huán)境的配置與Eclipse的安裝過程

    這篇文章主要介紹了Java工作環(huán)境的配置與Eclipse的安裝過程,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • 詳解java一維數(shù)組及練習(xí)題實例

    詳解java一維數(shù)組及練習(xí)題實例

    在本篇文章里小編給大家整理了關(guān)于java一維數(shù)組及練習(xí)題的相關(guān)知識點和實例代碼,有需要的朋友們跟著學(xué)習(xí)下。
    2019-07-07
  • SpringMVC表單標簽使用詳解

    SpringMVC表單標簽使用詳解

    這篇文章主要為大家詳細介紹了SpringMVC表單標簽的使用方法,教大家如何用Spring封裝的一系列表單標簽
    2017-03-03

最新評論

图片| 奇台县| 永和县| 崇信县| 金山区| 浦江县| 仁化县| 宜章县| 鹤岗市| 桦川县| 西和县| 南郑县| 吉首市| 太和县| 同江市| 达尔| 永登县| 云阳县| 浮山县| 甘谷县| 贵州省| 南溪县| 辽源市| 岑巩县| 夏邑县| 万宁市| 土默特左旗| 道真| 德安县| 湖北省| 嘉峪关市| 靖安县| 弥勒县| 中西区| 江源县| 松原市| 武义县| 平原县| 驻马店市| 耒阳市| 鄄城县|