欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Google Guava 緩存工具使用詳解

 更新時間:2023年12月22日 11:24:39   作者:騎個小蝸牛  
這篇文章主要介紹了Guava自加載緩存LoadingCache使用指南,通過這些內(nèi)容介紹,了解了LoadingCache的基本原理和用法,包括如何創(chuàng)建和配置緩存,以及如何結(jié)合Java?8的特性來優(yōu)化代碼,需要的朋友可以參考下

Google Guava 緩存工具使用詳解

緩存工具

Guava提供了Cache接口和相關的類來支持緩存功能,它提供了高性能、線程安全的內(nèi)存緩存,可以用于優(yōu)化應用程序的性能。

特點:

  • 自動回收過期數(shù)據(jù)
  • 支持緩存項的定時回收
  • 支持緩存項的最大數(shù)量限制
  • 支持緩存項的大小限制
  • 支持緩存項的權(quán)重限制,簡化開發(fā)者實現(xiàn)基于容量的限制
  • 提供了統(tǒng)計信息

相關接口類:

接口/類描述
Cache<K, V>接口表示一種能夠存儲鍵值對的緩存結(jié)構(gòu)
LoadingCache<K, V>是 Cache 接口的子接口,用于在緩存中自動加載緩存項
CacheLoader<K, V>在使用 LoadingCache 時提供加載緩存項的邏輯
CacheBuilder用于創(chuàng)建 CacheLoadingCache 實例的構(gòu)建器類
CacheStats用于表示緩存的統(tǒng)計信息,如命中次數(shù)、命中率、加載次數(shù)、存儲次數(shù)等
RemovalListener<K, V>用于監(jiān)聽緩存條目被移除的事件,并在條目被移除時執(zhí)行相應的操作

使用示例:

    public static void main(String[] args) throws Exception {
        // 創(chuàng)建Cache實例
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .initialCapacity(2)                                         // 設置初始容量
                .concurrencyLevel(4)                                        // 設置并發(fā)級別
                .maximumSize(5)                                             // 設置最大容量
//                .maximumWeight(1000)                                        // 設置最大權(quán)重
//                .weigher((Weigher<String, String>) (k, v) -> v.length())    // 設置權(quán)重計算器
                .expireAfterWrite(Duration.ofSeconds(3))                    // 寫入后3秒過期
                .expireAfterAccess(Duration.ofSeconds(20))                  // 訪問后20秒過期
                .refreshAfterWrite(Duration.ofSeconds(10))                  // 寫入后自動刷新,3秒刷新一次
                .recordStats()                                              // 開啟統(tǒng)計信息記錄
                .removalListener(notification -> {                          // 設置移除監(jiān)聽
                    // 緩存Key被移除時觸發(fā)
                    String cause = "";
                    if (RemovalCause.EXPLICIT.equals(notification.getCause())) {
                        cause = "被顯式移除";
                    } else if (RemovalCause.REPLACED.equals(notification.getCause())) {
                        cause = "被替換";
                    } else if (RemovalCause.EXPIRED.equals(notification.getCause())) {
                        cause = "被過期移除";
                    } else if (RemovalCause.SIZE.equals(notification.getCause())) {
                        cause = "被緩存條數(shù)超上限移除";
                    } else if (RemovalCause.COLLECTED.equals(notification.getCause())) {
                        cause = "被垃圾回收移除";
                    }
                    System.out.println(DateUtil.formatDateTime(new Date()) + " Key: " + notification.getKey() + " 移除了, 移除原因: " + cause);
                })
                .build(new CacheLoader<String, String>() {                  // 設置緩存重新加載邏輯
                    @Override
                    public String load(String key) {
                        // 重新加載指定Key的值
                        String newValue = "value" + (int)Math.random()*100;
                        System.out.println(DateUtil.formatDateTime(new Date()) + " Key: " + key + " 重新加載,新value:" + newValue);
                        return newValue;
                    }
                });
        // 將數(shù)據(jù)放入緩存
        cache.put("key0", "value0");
        cache.invalidate("key0");
        cache.put("key1", "value1");
        cache.put("key1", "value11");
        cache.put("key2", "value22");
        cache.put("key3", "value3");
        cache.put("key4", "value4");
        cache.put("key5", "value5");
        cache.put("key6", "value6");
        cache.put("key7", "value7");
        cache.put("key8", "value8");
        while (true) {
            // 獲取數(shù)據(jù)
            System.out.println(DateUtil.formatDateTime(new Date()) + " get key1 value: " + cache.get("key1"));
            // 統(tǒng)計信息
            System.out.println(DateUtil.formatDateTime(new Date()) + " get stats: " + cache.stats());
            Thread.sleep(1000);
        }
    }

打印日志:

2023-11-24 15:48:17 Key: key0 移除了, 移除原因: 被顯式移除
2023-11-24 15:48:17 Key: key1 移除了, 移除原因: 被替換
2023-11-24 15:48:17 Key: key1 移除了, 移除原因: 被緩存條數(shù)超上限移除
2023-11-24 15:48:17 Key: key2 移除了, 移除原因: 被緩存條數(shù)超上限移除
2023-11-24 15:48:17 Key: key3 移除了, 移除原因: 被緩存條數(shù)超上限移除
2023-11-24 15:48:17 Key: key1 重新加載,新value:value0
2023-11-24 15:48:17 Key: key4 移除了, 移除原因: 被緩存條數(shù)超上限移除
2023-11-24 15:48:17 get key1 value: value0
2023-11-24 15:48:17 get stats: CacheStats{hitCount=0, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:18 get key1 value: value0
2023-11-24 15:48:18 get stats: CacheStats{hitCount=1, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:19 get key1 value: value0
2023-11-24 15:48:19 get stats: CacheStats{hitCount=2, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:20 Key: key5 移除了, 移除原因: 被過期移除
2023-11-24 15:48:20 Key: key6 移除了, 移除原因: 被過期移除
2023-11-24 15:48:20 Key: key7 移除了, 移除原因: 被過期移除
2023-11-24 15:48:20 Key: key8 移除了, 移除原因: 被過期移除
2023-11-24 15:48:20 Key: key1 移除了, 移除原因: 被過期移除
2023-11-24 15:48:20 Key: key1 重新加載,新value:value0
2023-11-24 15:48:20 get key1 value: value0
2023-11-24 15:48:20 get stats: CacheStats{hitCount=2, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:21 get key1 value: value0
2023-11-24 15:48:21 get stats: CacheStats{hitCount=3, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:22 get key1 value: value0
2023-11-24 15:48:22 get stats: CacheStats{hitCount=4, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:23 Key: key1 移除了, 移除原因: 被過期移除
2023-11-24 15:48:23 Key: key1 重新加載,新value:value0
2023-11-24 15:48:23 get key1 value: value0
2023-11-24 15:48:23 get stats: CacheStats{hitCount=4, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
2023-11-24 15:48:24 get key1 value: value0
2023-11-24 15:48:24 get stats: CacheStats{hitCount=5, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
2023-11-24 15:48:25 get key1 value: value0
2023-11-24 15:48:25 get stats: CacheStats{hitCount=6, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
......

Cache接口

Cache接口是Guava緩存的核心接口,定義了緩存的基本操作。

它是使用 CacheBuilder 創(chuàng)建的。它提供了基本的緩存操作,如 put、get、delete等方法。同時,Cache 還提供了諸如統(tǒng)計信息、緩存項的值獲取方式、緩存項的失效、緩存項的回收等方法,可以滿足大多數(shù)應用的需求。

主要方法:

方法描述
V get(K key, Callable<? extends V> valueLoader)根據(jù)鍵獲取對應的緩存值,如果緩存中不存在該鍵,會使用 valueLoader 加載并存儲該值。
V getIfPresent(K key)根據(jù)鍵獲取對應的緩存值,如果不存在則返回 null。
Map<K, V> getAllPresent(Iterable<?> keys)獲取多個鍵對應的緩存值的映射,如果緩存中不存在某個鍵,則該鍵不會出現(xiàn)在返回的映射中。
void put(K key, V value)將鍵值對放入緩存中。如果鍵已經(jīng)存在,則會替換對應的值。
void putAll(Map<? extends K, ? extends V> map)將多個鍵值對添加到緩存中。
void invalidate(Object key)根據(jù)鍵從緩存中移除條目。
void invalidateAll(Iterable<?> keys)根據(jù)鍵集合移除多個條目。
void invalidateAll()移除緩存中的所有條目。
long size()返回緩存中的條目數(shù)。
CacheStats stats()返回緩存的統(tǒng)計信息。
ConcurrentMap<K, V> asMap()返回緩存的并發(fā)映射視圖。
void cleanUp()執(zhí)行緩存的清理操作。

LoadingCache接口

LoadingCache 繼承自 Cache 接口,它是一個帶有自動加載功能的緩存接口。

在使用 LoadingCache 時,如果緩存中不存在所需的鍵值對,則會自動調(diào)用CacheLoader的加載方法進行加載,并將加載的結(jié)果存入緩存中。

主要方法:

方法說明
get(K key)根據(jù)指定的鍵檢索值,如果鍵不存在,將調(diào)用 CacheLoader 進行加載并返回對應的值
getAll(Iterable<? extends K> keys)根據(jù)給定的鍵集合批量檢索值,并返回一個 Map 對象,對于已緩存的鍵將直接返回對應的值,對于未緩存的鍵將通過 CacheLoader 進行加載。
getUnchecked(K key)獲取指定鍵對應的值,如果緩存中不存在該鍵,則返回 null,不會觸發(fā)CacheLoader 加載。
refresh(K key)刷新指定鍵對應的值,即使用 CacheLoader 重新加載該鍵對應的值,并更新緩存。

CacheBuilder類

CacheBuilder類是用于創(chuàng)建Guava緩存的構(gòu)建器??梢允褂迷擃惖膎ewBuilder()方法創(chuàng)建一個構(gòu)建器實例,并通過一系列方法設置緩存的屬性,例如最大容量、過期時間等。最后可以通過build()方法構(gòu)建一個Cache實例。

主要方法:

方法說明
newBuilder()創(chuàng)建一個新的 CacheBuilder 實例
from(CacheBuilderSpec spec)根據(jù)給定的規(guī)范字符串創(chuàng)建一個 CacheBuilder 實例
from(String spec)根據(jù)給定的規(guī)范字符串創(chuàng)建一個 CacheBuilder 實例
initialCapacity(int initialCapacity)設置緩存的初始容量
concurrencyLevel(int concurrencyLevel)設置并發(fā)級別,用于估計同時寫入的線程數(shù)
maximumSize(long maximumSize)設置緩存的最大容量
maximumWeight(long maximumWeight)設置緩存的最大權(quán)重
weigher(Weigher<? super K1, ? super V1> weigher)設置緩存的權(quán)重計算器
weakKeys()使用弱引用存儲緩存鍵(例如,鍵的引用沒有被其他對象引用時,可以被垃圾回收)
weakValues()使用弱引用存儲緩存值(例如,值的引用沒有被其他對象引用時,可以被垃圾回收)
softValues()使用軟引用存儲緩存值(例如,當內(nèi)存不足時,可以被垃圾回收)
expireAfterWrite(java.time.Duration duration)設置寫入后過期時間
expireAfterWrite(long duration, TimeUnit unit)設置寫入后過期時間
expireAfterAccess(java.time.Duration duration)設置訪問后過期時間
expireAfterAccess(long duration, TimeUnit unit)設置訪問后過期時間
refreshAfterWrite(java.time.Duration duration)設置寫入后自動刷新時間
refreshAfterWrite(long duration, TimeUnit unit)設置寫入后自動刷新時間
ticker(Ticker ticker)設置用于衡量緩存時間的時鐘源
removalListener(RemovalListener<? super K1, ? super V1> listener)設置緩存條目移除監(jiān)聽器
recordStats()開啟緩存統(tǒng)計信息記錄
build(CacheLoader<? super K1, V1> loader)使用指定的 CacheLoader 構(gòu)建緩存
build()構(gòu)建緩存,如果沒有指定 CacheLoader,則需要使用 get 方法手動加載緩存項

部分方法詳解:

initialCapacity:設置緩存的初始容量

這個方法將通過一個整數(shù)值設置緩存的初始大小。它是一個可選的方法,如果沒有指定,緩存將采用默認的初始容量。

concurrencyLevel:設置并發(fā)級別

用于估計同時寫入的線程數(shù)。這個方法將通過一個整數(shù)值設置并發(fā)級別,用于內(nèi)部數(shù)據(jù)結(jié)構(gòu)的調(diào)整,以提高并發(fā)寫入的性能。它是一個可選的方法,缺省值為 4。

maximumSize:設置緩存的最大容量

這個方法將通過一個 long 類型的值設置緩存的最大容量。當緩存的條目數(shù)達到這個容量時,會觸發(fā)緩存清除策略來移除一些條目以騰出空間。它是一個可選的方法,如果沒有指定最大容量,緩存將不會有大小限制。

maximumWeight:設置緩存的最大權(quán)重

這個方法將通過一個 long 類型的值設置緩存的最大權(quán)重。權(quán)重可以根據(jù)緩存條目的大小計算,通常用于緩存對象大小不同的場景。當緩存的總權(quán)重達到這個值時,會觸發(fā)緩存清除策略來移除一些條目以騰出空間。它是一個可選的方法,如果沒有指定最大權(quán)重,緩存將不會有權(quán)重限制。

weigher:設置緩存的權(quán)重計算器

這個方法將通過一個實現(xiàn)了 Weigher 接口的對象設置緩存的權(quán)重計算器。通過權(quán)重計算器,可以根據(jù)緩存條目的鍵和值來計算它們的權(quán)重,以便在達到最大權(quán)重時觸發(fā)緩存清除策略。它是一個可選的方法,如果沒有設置權(quán)重計算器,緩存將不會有權(quán)重限制。

expireAfterWrite:設置寫入后過期時間

這個方法通過一個 java.time.Duration 對象設置緩存條目的寫入后過期時間。過期時間從最后一次寫入條目開始計算。一旦超過指定的時間,條目將被認為是過期的并被清除。這是一個可選的方法,如果沒有指定過期時間,條目將不會主動過期。

expireAfterAccess:設置訪問后過期時間

這個方法通過一個 java.time.Duration 對象設置緩存條目的訪問后過期時間。過期時間從最后一次訪問條目開始計算。一旦超過指定的時間,條目將被認為是過期的并被清除。這是一個可選的方法,如果沒有指定過期時間,條目將不會主動過期。

refreshAfterWrite:設置寫入后自動刷新時間

這個方法通過一個 java.time.Duration 對象設置緩存條目的自動刷新時間。自動刷新時間從最后一次寫入條目開始計算。一旦超過指定的時間,當條目被訪問時,緩存將自動刷新該條目,即會調(diào)用 CacheLoader 的 load 方法重新加載該條目。這是一個可選的方法,如果沒有設置自動刷新時間,條目將不會自動刷新。

recordStats():開啟緩存統(tǒng)計信息記錄

這個方法用于開啟緩存的統(tǒng)計信息記錄功能。一旦開啟,可以通過 Cache.stats() 方法獲取緩存的統(tǒng)計信息,如命中率、加載次數(shù)、平均加載時間等。這是一個可選的方法,如果不開啟統(tǒng)計信息記錄,將無法獲取緩存的統(tǒng)計信息。

注意事項:

  • maximumSize與maximumWeight不能同時設置
  • 設置maximumWeight時必須設置weigher
  • 當緩存失效后,refreshAfterWrite設置的寫入后自動刷新時間不會再有用
  • 注意:expireAfterWrite、expireAfterAccess、refreshAfterWrite三個值的使用
  • 開啟recordStats后,才進行統(tǒng)計

CacheLoader類

CacheLoader 可以被視為一種從存儲系統(tǒng)(如磁盤、數(shù)據(jù)庫或遠程節(jié)點)中加載數(shù)據(jù)的方法。

CacheLoader 通常搭配refreshAfterWrite使用,在寫入指定的時間周期后會調(diào)用CacheLoader 的load方法來獲取并刷新為新值。

load 方法在以下情況下會被觸發(fā)調(diào)用:

  • 當設置了refreshAfterWrite(寫入后自動刷新時間),達到自動刷新時間時,會調(diào)用 load 方法來重新加載該鍵的值。
  • 調(diào)用 Cache.get(key) 方法獲取緩存中指定鍵的值時,如果該鍵的值不存在,則會調(diào)用 load 方法來加載該鍵的值。
  • 調(diào)用 Cache.get(key, callable) 方法獲取緩存中指定鍵的值時,如果該鍵的值存在,則直接返回;如果該鍵的值不存在,則會調(diào)用 callable 參數(shù)指定的回調(diào)函數(shù)來計算并加載該鍵的值。
  • 調(diào)用 Cache.getUnchecked(key) 方法獲取緩存中指定鍵的值時,無論該鍵的值存在與否,都會調(diào)用 load 方法來加載該鍵的值。

需要注意的是,當調(diào)用 load 方法加載緩存值時,可能會發(fā)生 IO 操作或其他耗時操作,因此建議在加載操作中使用異步方式來避免阻塞主線程。另外,加載操作的實現(xiàn)要考慮緩存的一致性和并發(fā)性,避免多個線程同時加載同一個鍵的值。

CacheStats類

CacheStats 對象提供了諸如緩存命中率、加載緩存項數(shù)、緩存項回收數(shù)等統(tǒng)計信息的訪問。

它可以通過 Cache.stats() 方法來獲取,從而方便開發(fā)者監(jiān)控緩存狀態(tài)。

主要屬性:

屬性描述
hitCount緩存命中次數(shù)。表示從緩存中成功獲取數(shù)據(jù)的次數(shù)
missCount緩存未命中次數(shù)。表示從緩存中未能獲取到數(shù)據(jù)的次數(shù)
loadSuccessCount加載數(shù)據(jù)成功次數(shù)。表示通過 CacheLoader 成功加載數(shù)據(jù)的次數(shù)
loadExceptionCount加載數(shù)據(jù)異常次數(shù)。表示通過 CacheLoader 加載數(shù)據(jù)時發(fā)生異常的次數(shù)
totalLoadTime加載數(shù)據(jù)總耗時。表示通過 CacheLoader 加載數(shù)據(jù)的總時間
evictionCount緩存項被移除的次數(shù),只記錄因空超過設置的最大容量而進行緩存項移除的次數(shù)

RemovalListener類

RemovalListener 用于在緩存中某個值被移除時執(zhí)行相應的回調(diào)操作。

可以使用 CacheBuilder.removalListener() 方法為緩存設置 RemovalListener。

RemovalListener 的使用:

  • 創(chuàng)建一個實現(xiàn) RemovalListener 接口的類,實現(xiàn) onRemoval 方法。這個方法會在緩存項被移除時被調(diào)用,接受兩個參數(shù): key 和 value。key 是被移除的緩存項的鍵,value 是被移除的緩存項的值。你可以根據(jù)需要在 onRemoval 方法中實現(xiàn)自定義的邏輯。
  • 使用 CacheBuilder 的 removalListener 方法,將創(chuàng)建的 RemovalListener 對象傳遞給它。
  • 緩存項被移除時,onRemoval 方法會自動被調(diào)用,方法會傳入一個RemovalNotification 類型的參數(shù),里面包含相應的 key 和 value等信息。你可以在這個方法中執(zhí)行自定義的業(yè)務邏輯,例如日志記錄、資源清理等操作。

RemovalNotification:

RemovalNotification 是 Guava 中用于表示緩存項被移除的通知的類。當在 Guava Cache 中注冊了 RemovalListener 后,RemovalNotification 對象會在緩存項被移除時傳遞給 RemovalListener 的 onRemoval 方法。

RemovalNotification 包含了有關被移除緩存項的一些重要信息,例如鍵、值以及移除原因。下面是 RemovalNotification 類中常用的屬性和方法:

  • getKey():獲取被移除的緩存項的鍵。
  • getValue():獲取被移除的緩存項的值。
  • getCause():獲取移除原因,它是一個枚舉類型RemovalCause,表示緩存項被移除的原因。

RemovalCause的可選值:

  • EXPLICIT:條目被顯式刪除,例如通過調(diào)用 Cache.invalidate(key) 方法。
  • REPLACED:條目被替換,例如通過調(diào)用 Cache.put(key, value) 方法重復放入相同的鍵。
  • EXPIRED:緩存條目由于達到了指定的過期時間而被移除。
  • SIZE:緩存條目由于超過了指定的大小限制而被移除。
  • COLLECTED:緩存條目被垃圾回收移除。這是在啟用了緩存值的弱引用或軟引用時發(fā)生的。

使用 RemovalNotification 可以讓你在緩存項被移除時獲取相關信息,并根據(jù)移除原因采取適當?shù)奶幚泶胧?。例如,你可以根?jù)移除原因記錄日志、執(zhí)行清理操作、發(fā)送通知等。這樣能夠增強緩存的功能和可觀察性。

注意事項:

  • RemovalListener 的 onRemoval 方法會在移除操作發(fā)生時同步調(diào)用,因此請確保不要在此方法中做耗時的操作,以免阻塞緩存的性能。
  • 如果在緩存移除過程中拋出任何異常,它將被捕獲并記錄,不會影響緩存的正常運行。
  • 需要注意的是,RemovalListener 只會在通過 Cache 的操作(如 invalidate、invalidateAll、put 進行替換)觸發(fā)移除時被調(diào)用,并不會在緩存項因為過期失效而自動移除時被調(diào)用。

使用 RemovalListener 可以方便地在緩存項被移除時執(zhí)行一些自定義的操作,例如清理相關資源、更新其他緩存或發(fā)送通知等。根據(jù)實際需要,合理利用 RemovalListener 可以增強緩存的功能和靈活性。

補充:

Guava自加載緩存LoadingCache使用指南實戰(zhàn)案例

第1章:引言

大家好,我是小黑,今天我們來聊聊緩存。在Java世界里,高效的緩存機制對于提升應用性能、降低數(shù)據(jù)庫負擔至關重要。想象一下,如果每次數(shù)據(jù)請求都要跑到數(shù)據(jù)庫里取,那服務器豈不是要累趴了?這時候,緩存就顯得尤為重要了。

那么,怎么實現(xiàn)一個既高效又好用的緩存呢?別急,咱們今天的主角——Guava的LoadingCache就是這樣一個神器。LoadingCache,顧名思義,就是能夠自動加載緩存的工具。它不僅能自動載入數(shù)據(jù),還能按需刷新,簡直是懶人救星!接下來,小黑就帶大家一起深入探究Guava的這個強大功能。

第2章:Guava簡介

Guava是Google開源的一款Java庫,提供了一堆好用的工具類,從集合操作、緩存機制到函數(shù)式編程,應有盡有。使用Guava,咱們可以寫出更簡潔、更高效、更優(yōu)雅的Java代碼。今天,小黑重點要聊的是Guava中的緩存部分。

首先,讓我們來看看Guava緩存的一個基本概念:LoadingCache。LoadingCache是Guava中一個提供自動加載功能的緩存接口。它允許咱們通過一個CacheLoader來指定如何加載緩存。這就意味著,當咱們嘗試從緩存中讀取一個值,如果這個值不存在,LoadingCache就會自動調(diào)用預定義的加載機制去獲取數(shù)據(jù),然后將其加入到緩存中,非常智能。

來,小黑先給大家展示一個簡單的LoadingCache創(chuàng)建示例:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class LoadingCacheExample {
    public static void main(String[] args) {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100) // 最大緩存項數(shù)
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        return "Hello, " + key; // 定義緩存加載的方式
                    }
                });
        System.out.println(cache.getUnchecked("Guava")); // 輸出:Hello, Guava
    }
}

在這個例子里,小黑創(chuàng)建了一個簡單的LoadingCache實例。當咱們嘗試通過getUnchecked方法獲取一個緩存項時,如果這個項不存在,CacheLoader會自動加載一個新值。在這里,它就是簡單地返回一個字符串。

第3章:LoadingCache基礎

什么是LoadingCache呢?簡單來說,它是Guava提供的一個緩存接口,能夠自動加載緩存。當你嘗試從緩存中讀取一個值時,如果這個值不存在,LoadingCache會自動調(diào)用預定義的加載邏輯來獲取這個值,然后存儲到緩存中。這個過程完全自動化,省去了很多手動管理緩存的麻煩。

那么,LoadingCache的核心特性是什么呢?首先,它提供了自動的緩存加載機制,這意味著咱們不需要自己去寫代碼判斷緩存是否存在或者過期。其次,它支持多種緩存過期策略,比如基于時間的過期、大小限制等,確保緩存的有效性。再者,LoadingCache還提供了緩存統(tǒng)計和監(jiān)聽的功能,方便咱們監(jiān)控和調(diào)優(yōu)緩存的使用。

來,讓小黑用一個例子來展示一下LoadingCache的基本用法:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.ExecutionException;
public class LoadingCacheDemo {
    public static void main(String[] args) throws ExecutionException {
        // 創(chuàng)建一個CacheLoader
        CacheLoader<String, String> loader = new CacheLoader<String, String>() {
            @Override
            public String load(String key) {
                return key.toUpperCase(); // 模擬加載數(shù)據(jù)的過程
            }
        };
        // 使用CacheBuilder構(gòu)建一個LoadingCache
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100) // 設置最大緩存數(shù)為100
                .build(loader);
        // 使用緩存
        System.out.println(cache.get("hello")); // 輸出: HELLO
        System.out.println(cache.get("guava")); // 輸出: GUAVA
    }
}

在這個例子中,小黑創(chuàng)建了一個CacheLoader來定義加載數(shù)據(jù)的邏輯,這里就是簡單地將字符串轉(zhuǎn)換為大寫。然后,使用CacheBuilder來構(gòu)建一個LoadingCache實例,設置了最大緩存數(shù)為100。當調(diào)用get方法時,如果緩存中不存在對應的鍵值,LoadingCache會自動調(diào)用CacheLoader來加載數(shù)據(jù),并將結(jié)果存入緩存。

第4章:創(chuàng)建LoadingCache

創(chuàng)建一個LoadingCache最關鍵的就是定義一個CacheLoader。這個CacheLoader指定了如何加載緩存。它就像是個工廠,當咱們請求的數(shù)據(jù)在緩存中不存在時,它就會生產(chǎn)出所需的數(shù)據(jù)。

那么,怎么定義這個CacheLoader呢?讓小黑給你看個例子:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class UserCache {
    // 假設有一個用戶服務,用于獲取用戶信息
    private static UserService userService = new UserService();
    public static void main(String[] args) throws Exception {
        // 創(chuàng)建CacheLoader
        CacheLoader<String, User> loader = new CacheLoader<String, User>() {
            @Override
            public User load(String userId) {
                // 從用戶服務獲取用戶信息
                return userService.getUserById(userId);
            }
        };
        // 創(chuàng)建LoadingCache
        LoadingCache<String, User> cache = CacheBuilder.newBuilder()
                .maximumSize(100) // 設置最大緩存數(shù)
                .build(loader);
        // 使用緩存獲取用戶信息
        User user = cache.get("123"); // 如果緩存中沒有,會調(diào)用load方法加載數(shù)據(jù)
        System.out.println(user);
    }
}

在這個例子中,小黑創(chuàng)建了一個CacheLoader來從用戶服務中獲取用戶信息。然后,使用CacheBuilder來構(gòu)建一個LoadingCache,并設置了最大緩存數(shù)量為100。當咱們通過get方法獲取用戶信息時,如果緩存中沒有相應的數(shù)據(jù),CacheLoader就會自動加載數(shù)據(jù)。

這個過程聽起來是不是很神奇?實際上,這背后是一種非常有效的數(shù)據(jù)管理策略。通過這種方式,咱們可以減少對數(shù)據(jù)庫或遠程服務的直接訪問,提高了應用的響應速度和效率。

第5章:LoadingCache的高級特性 自動加載和刷新機制

首先,LoadingCache的一個很棒的功能就是自動加載和刷新。這意味著當咱們請求某個鍵的值時,如果這個值不存在或者需要刷新,LoadingCache會自動調(diào)用CacheLoader去加載或刷新數(shù)據(jù)。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.TimeUnit;
public class AutoRefreshCache {
    public static void main(String[] args) throws Exception {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .refreshAfterWrite(1, TimeUnit.MINUTES) // 設置1分鐘后刷新
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) {
                        return fetchDataFromDatabase(key); // 模擬從數(shù)據(jù)庫加載數(shù)據(jù)
                    }
                });
        // 使用緩存
        System.out.println(cache.get("key1")); // 第一次加載
        // 1分鐘后,嘗試再次獲取,將觸發(fā)刷新操作
    }
    private static String fetchDataFromDatabase(String key) {
        // 模擬數(shù)據(jù)庫操作
        return "Data for " + key;
    }
}

在這個例子中,咱們設置了refreshAfterWrite,這意味著每當一個鍵值對寫入一分鐘后,它就會被自動刷新。

處理異常值

有時候,加載數(shù)據(jù)可能會出現(xiàn)異常。LoadingCache提供了優(yōu)雅的處理異常的機制。

public class ExceptionHandlingCache {
    public static void main(String[] args) throws Exception {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        if ("errorKey".equals(key)) {
                            throw new Exception("Loading error");
                        }
                        return "Data for " + key;
                    }
                });
        try {
            System.out.println(cache.get("errorKey"));
        } catch (Exception e) {
            System.out.println("Error during cache load: " + e.getMessage());
        }
    }
}

這里,如果加載過程中出現(xiàn)異常,咱們可以捕獲這個異常,并做適當?shù)奶幚怼?/p>

統(tǒng)計和監(jiān)聽功能

LoadingCache還提供了緩存統(tǒng)計和監(jiān)聽功能,這對于監(jiān)控緩存性能和行為非常有用。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
public class CacheMonitoring {
    public static void main(String[] args) throws Exception {
        RemovalListener<String, String> removalListener = new RemovalListener<String, String>() {
            @Override
            public void onRemoval(RemovalNotification<String, String> notification) {
                System.out.println("Removed: " + notification.getKey() + ", Cause: " + notification.getCause());
            }
        };
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100)
                .removalListener(removalListener)
                .build(new CacheLoader<String, String>() {
                    // ...
                });
        // 使用緩存
        // ...
    }
}

在這個例子中,小黑設置了一個RemovalListener,用于監(jiān)聽緩存項的移除事件。

第6章:LoadingCache的最佳實踐 配置緩存大小

合理配置緩存大小非常關鍵。如果緩存太小,就會頻繁地加載數(shù)據(jù),影響性能;如果太大,又可能消耗過多內(nèi)存。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .maximumSize(1000) // 設置最大緩存項為1000
        .build(new CacheLoader<String, String>() {
            // ...
        });

在這個例子中,小黑設置了最大緩存項為1000。這個值需要根據(jù)實際情況和資源限制來調(diào)整。

設置合適的過期策略

LoadingCache支持基于時間的過期策略,比如訪問后過期和寫入后過期。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES) // 寫入后10分鐘過期
        .expireAfterAccess(5, TimeUnit.MINUTES) // 訪問后5分鐘過期
        .build(new CacheLoader<String, String>() {
            // ...
        });

選擇合適的過期策略可以確保緩存中的數(shù)據(jù)既不會過時,又能有效利用內(nèi)存。

異常處理策略

在加載數(shù)據(jù)時可能會遇到各種異常。咱們可以設置一個合理的異常處理策略,比如記錄日志、返回默認值或者重新拋出異常。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .build(new CacheLoader<String, String>() {
            @Override
            public String load(String key) throws Exception {
                try {
                    return fetchData(key);
                } catch (Exception e) {
                    // 處理異常
                }
            }
        });

使用軟引用或弱引用

為了防止緩存占用過多內(nèi)存,可以使用軟引用或弱引用。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .softValues() // 使用軟引用存儲值
        .weakKeys() // 使用弱引用存儲鍵
        .build(new CacheLoader<String, String>() {
            // ...
        });

使用軟引用和弱引用可以幫助Java垃圾收集器在需要時回收緩存項,防止內(nèi)存泄露。

監(jiān)聽移除通知

設置移除監(jiān)聽器可以幫助咱們了解緩存的行為,比如為什么某個項被移除。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .removalListener(notification -> {
            // 處理移除通知
        })
        .build(new CacheLoader<String, String>() {
            // ...
        });

通過這些最佳實踐,咱們可以確保LoadingCache的高效運行,同時避免一些常見的問題。這樣,咱們的Java應用就能更加穩(wěn)定和高效地運行啦!

第7章:LoadingCache與Java 8的結(jié)合

好的,咱們接下來聊聊怎樣把LoadingCache和Java 8的特性結(jié)合起來,用起來更順手。

Java 8引入了很多強大的新特性,像Lambda表達式、Stream API等,這些都可以和LoadingCache搭配使用,讓代碼更簡潔、更易讀。

使用Lambda表達式簡化CacheLoader

首先,咱們可以用Lambda表達式來簡化CacheLoader的創(chuàng)建。這樣代碼看起來更干凈,更直觀。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .build(key -> fetchDataFromDatabase(key)); // 使用Lambda表達式
private static String fetchDataFromDatabase(String key) {
    // 數(shù)據(jù)庫操作
    return "Data for " + key;
}

在這個例子里,小黑用Lambda表達式替代了傳統(tǒng)的匿名內(nèi)部類,使代碼更加簡潔。

結(jié)合Stream API處理緩存數(shù)據(jù)

接下來,咱們看看如何用Java 8的Stream API來處理LoadingCache中的數(shù)據(jù)。

LoadingCache<String, User> userCache = //... 緩存初始化
List<String> userIds = //... 用戶ID列表
// 使用Stream API獲取用戶信息列表
List<User> users = userIds.stream()
        .map(userId -> userCache.getUnchecked(userId))
        .collect(Collectors.toList());

在這個例子中,小黑用Stream API來處理一系列用戶ID,然后用map方法從緩存中獲取對應的用戶信息。

利用Optional處理緩存返回值

最后,Java 8引入的Optional也可以用來優(yōu)雅地處理可能為空的緩存返回值。

public Optional<User> getUser(String userId) {
    try {
        return Optional.ofNullable(userCache.get(userId));
    } catch (Exception e) {
        return Optional.empty();
    }
}

在這里,小黑用Optional包裝了緩存的返回值。這樣一來,就能優(yōu)雅地處理緩存可能返回的空值情況。

通過這些方式,結(jié)合Java 8的特性,咱們可以讓LoadingCache的使用更加高效和優(yōu)雅。這不僅提高了代碼的可讀性,還讓咱們的編程體驗更加流暢。

第8章:實戰(zhàn)案例

小黑將通過一個具體的例子,展示如何在實際項目中使用LoadingCache。這個例子會模擬一個簡單的場景,比如說,使用LoadingCache來緩存用戶的登錄次數(shù)。

假設咱們有一個應用,需要跟蹤用戶的登錄次數(shù)。每次用戶登錄時,程序會增加其登錄次數(shù)。為了提高性能,咱們用LoadingCache來緩存這些數(shù)據(jù),避免每次都查詢數(shù)據(jù)庫。

首先,小黑定義了一個模擬的用戶登錄服務:

public Optional<User> getUser(String userId) {
    try {
        return Optional.ofNullable(userCache.get(userId));
    } catch (Exception e) {
        return Optional.empty();
    }
}

這個UserService類有一個addLoginCount方法,用于增加特定用戶的登錄次數(shù)。

接下來,小黑將展示如何使用LoadingCache來緩存登錄次數(shù):

LoadingCache<String, Integer> loginCache = CacheBuilder.newBuilder()
        .expireAfterAccess(30, TimeUnit.MINUTES) // 設置緩存30分鐘后過期
        .build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String userId) {
                return userService.addLoginCount(userId);
            }
        });
public void userLogin(String userId) {
    int count = loginCache.getUnchecked(userId);
    System.out.println("User " + userId + " login count: " + count);
}

在這個例子中,每當有用戶登錄,userLogin方法就會被調(diào)用。這個方法會從loginCache中獲取用戶的登錄次數(shù),如果緩存中沒有,CacheLoader會調(diào)用UserServiceaddLoginCount來獲取最新的計數(shù),然后將其存儲在緩存中。

第9章:總結(jié)

通過這些章節(jié),咱們了解了LoadingCache的基本原理和用法,包括如何創(chuàng)建和配置緩存,以及如何結(jié)合Java 8的特性來優(yōu)化代碼。LoadingCache不僅提供了自動加載和刷新的強大功能,還有異常處理、緩存統(tǒng)計和監(jiān)聽等高級特性。

實戰(zhàn)案例給咱們展示了LoadingCache在現(xiàn)實場景中的應用。不管是緩存用戶信息還是統(tǒng)計數(shù)據(jù),LoadingCache都能大大提高性能和用戶體驗。

到此這篇關于Guava自加載緩存LoadingCache使用指南的文章就介紹到這了,更多相關Guava LoadingCache緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • MyBatis與其使用方法示例詳解

    MyBatis與其使用方法示例詳解

    MyBatis是一個支持自定義SQL的持久層框架,通過XML文件實現(xiàn)SQL配置和數(shù)據(jù)映射,簡化了JDBC代碼的編寫,本文給大家介紹MyBatis與其使用方法講解,感興趣的朋友一起看看吧
    2025-03-03
  • Spring的注解配置與XML配置之間的比較

    Spring的注解配置與XML配置之間的比較

    在很多情況下,注釋配置比 XML 配置更受歡迎,注釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多注釋類,現(xiàn)在您已經(jīng)可以使用注釋配置完成大部分 XML 配置的功能
    2013-09-09
  • js-tab選項卡

    js-tab選項卡

    本文主要介紹了js-tab選項卡的示例代碼。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • Java包裝類之自動裝箱與拆箱

    Java包裝類之自動裝箱與拆箱

    這篇文章主要介紹了Java包裝類之自動裝箱與拆箱,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • 詳解Spring AOP的實現(xiàn)方式

    詳解Spring AOP的實現(xiàn)方式

    AOP是一種思想,是對某一類事情的集中處理,切面就是指某一類特定的問題,所以AOP可以理解為面向特定方法編程,這篇文章主要介紹了Spring AOP的實現(xiàn)方式,需要的朋友可以參考下
    2024-02-02
  • Spring MVC @GetMapping和@PostMapping注解的使用方式

    Spring MVC @GetMapping和@PostMapping注解的使用方式

    這篇文章主要介紹了Spring MVC @GetMapping和@PostMapping注解的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 解決Springboot項目報錯:java:錯誤:不支持發(fā)行版本?17

    解決Springboot項目報錯:java:錯誤:不支持發(fā)行版本?17

    這篇文章主要給大家介紹了關于解決Springboot項目報錯:java:錯誤:不支持發(fā)行版本17的相關資料,這個錯誤意味著你的Spring Boot項目正在使用Java 17這個版本,但是你的項目中未配置正確的Java版本,需要的朋友可以參考下
    2023-08-08
  • java基于QuartzJobBean實現(xiàn)定時功能的示例代碼

    java基于QuartzJobBean實現(xiàn)定時功能的示例代碼

    QuartzJobBean是Quartz框架中的一個抽象類,用于定義和實現(xiàn)可由Quartz調(diào)度的作業(yè),本文主要介紹了java基于QuartzJobBean實現(xiàn)定時功能的示例代碼,具有一定的參考價值,感興趣可以了解一下
    2023-09-09
  • SpringBoot中的application.properties無法加載問題定位技巧

    SpringBoot中的application.properties無法加載問題定位技巧

    這篇文章主要介紹了SpringBoot中的application.properties無法加載問題定位技巧,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java源碼解析之ConcurrentHashMap

    Java源碼解析之ConcurrentHashMap

    今天帶大家分析Java源碼,文中對Java ConcurrentHashMap介紹的非常詳細,有代碼示例,對正在學習Java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05

最新評論