Java實(shí)現(xiàn)本地緩存的四種方法實(shí)現(xiàn)與對(duì)比
本地緩存比如 caffine,guava cache 這些都是比較常用的,本地緩存的優(yōu)點(diǎn)就是速度非常快,沒(méi)有網(wǎng)絡(luò)消耗,缺點(diǎn)就是應(yīng)用重啟后,緩存就會(huì)丟失。
Java緩存技術(shù)可分為遠(yuǎn)端緩存和本地緩存,遠(yuǎn)端緩存常用的方案有著名的redis,而本地緩存的代表技術(shù)主要有HashMap,Guava Cache,Caffeine和Encahche。
1、HashMap
通過(guò)Map的底層方式,直接將需要緩存的對(duì)象放在內(nèi)存中。
- 優(yōu)點(diǎn):簡(jiǎn)單粗暴,不需要引入第三方包,比較適合一些比較簡(jiǎn)單的場(chǎng)景。
- 缺點(diǎn):沒(méi)有緩存淘汰策略,定制化開(kāi)發(fā)成本高。
package com.taiyuan.javademoone.cachedemo;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 定義一個(gè)基于LinkedHashMap實(shí)現(xiàn)的線程安全的LRU緩存類
*/
public class LRUCache extends LinkedHashMap<Object, Object> { // 明確泛型類型為<Object, Object>,提高代碼可讀性
/**
* 可重入讀寫(xiě)鎖,用于保證多線程環(huán)境下對(duì)緩存的并發(fā)讀寫(xiě)操作的安全性
*/
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock(); // 讀鎖,用于并發(fā)讀取時(shí)共享訪問(wèn)
private Lock writeLock = readWriteLock.writeLock(); // 寫(xiě)鎖,用于寫(xiě)入或修改時(shí)獨(dú)占訪問(wèn)
/**
* 緩存的最大容量限制,超過(guò)此容量將移除最久未使用的條目
*/
private int maxSize;
/**
* 構(gòu)造函數(shù),初始化LRU緩存并設(shè)置最大容量
*
* @param maxSize 緩存允許存儲(chǔ)的最大條目數(shù)
*/
public LRUCache(int maxSize) {
// 調(diào)用父類LinkedHashMap的構(gòu)造方法:
// 參數(shù)1:初始容量為maxSize + 1,避免頻繁擴(kuò)容
// 參數(shù)2:負(fù)載因子為1.0f,表示哈希表填滿到100%時(shí)才擴(kuò)容
// 參數(shù)3:accessOrder為true,表示按照訪問(wèn)順序排序,實(shí)現(xiàn)LRU策略
super(maxSize + 1, 1.0f, true);
this.maxSize = maxSize;
}
/**
* 重寫(xiě)get方法,獲取指定key對(duì)應(yīng)的value,使用讀鎖保證線程安全
*
* @param key 要查找的鍵
* @return 對(duì)應(yīng)的值,如果不存在則返回null
*/
@Override
public Object get(Object key) {
readLock.lock(); // 加讀鎖,允許多個(gè)線程同時(shí)讀
try {
return super.get(key); // 調(diào)用父類的get方法
} finally {
readLock.unlock(); // 確保讀鎖最終被釋放
}
}
/**
* 重寫(xiě)put方法,向緩存中添加或更新鍵值對(duì),使用寫(xiě)鎖保證線程安全
*
* @param key 要插入或更新的鍵
* @param value 要插入或更新的值
* @return 之前與key關(guān)聯(lián)的值,如果沒(méi)有則返回null
*/
@Override
public Object put(Object key, Object value) {
writeLock.lock(); // 加寫(xiě)鎖,確保同一時(shí)間只有一個(gè)線程可以寫(xiě)
try {
return super.put(key, value); // 調(diào)用父類的put方法
} finally {
writeLock.unlock(); // 確保寫(xiě)鎖最終被釋放
}
}
/**
* 重寫(xiě)removeEldestEntry方法,當(dāng)緩存大小超過(guò)maxSize時(shí),移除最久未使用的條目
*
* @param eldest 最久未訪問(wèn)的鍵值對(duì)Entry
* @return 如果當(dāng)前緩存大小超過(guò)最大容量,則返回true,觸發(fā)刪除最老的條目;否則返回false
*/
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > maxSize; // 判斷當(dāng)前緩存大小是否超出限制
}
// 測(cè)試主方法
public static void main(String[] args) {
LRUCache cache = new LRUCache(3);
cache.put("1", "one");
cache.put("2", "two");
cache.put("3", "three");
System.out.println(cache); // 輸出:{1=one, 2=two, 3=three}
}
}2、Guava Cache
Guava Cache 是 Google Guava 庫(kù)中的一個(gè)本地緩存實(shí)現(xiàn),它提供了以下主要特性:
- 自動(dòng)加載:當(dāng)緩存未命中時(shí)自動(dòng)從指定來(lái)源加載數(shù)據(jù)
- 多種淘汰策略:支持基于大小、時(shí)間和引用的淘汰
- 統(tǒng)計(jì)功能:內(nèi)置緩存命中率統(tǒng)計(jì)
- 線程安全:內(nèi)置并發(fā)控制機(jī)制
- 監(jiān)聽(tīng)器:支持緩存移除通知
<!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>33.4.8-jre</version> </dependency>
package com.helloworld.demo;
import com.google.common.cache.*;
import java.util.concurrent.TimeUnit;
public class GuavaCacheExample {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)Guava的LoadingCache實(shí)例,支持自動(dòng)加載緩存項(xiàng)
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100) // 設(shè)置緩存最大容量為100個(gè)條目
.expireAfterWrite(10, TimeUnit.MINUTES) // 設(shè)置緩存項(xiàng)在寫(xiě)入10分鐘后自動(dòng)過(guò)期
.recordStats() // 開(kāi)啟緩存統(tǒng)計(jì)功能,可以獲取命中率等信息
.build(new CacheLoader<String, String>() { // 定義緩存未命中時(shí)的加載邏輯
@Override
public String load(String key) throws Exception {
// 當(dāng)根據(jù)key獲取不到緩存值時(shí),調(diào)用此方法從數(shù)據(jù)源(如數(shù)據(jù)庫(kù))加載數(shù)據(jù)
return fetchDataFromDatabase(key);
}
});
try {
// 第一次獲取key為"user:1001"的值,由于緩存中沒(méi)有,會(huì)觸發(fā)load方法從數(shù)據(jù)庫(kù)加載
System.out.println("第一次獲取(從數(shù)據(jù)庫(kù)加載): " + cache.get("user:1001"));
// 第二次獲取相同的key,此時(shí)緩存中已有該值,直接從緩存返回,不會(huì)再次加載
System.out.println("第二次獲取(從緩存獲取): " + cache.get("user:1001"));
// 手動(dòng)向緩存中放入一個(gè)鍵值對(duì),繞過(guò)自動(dòng)加載邏輯
cache.put("user:1002", "Manual Data");
// 獲取手動(dòng)放入的緩存值
System.out.println("手動(dòng)放入的數(shù)據(jù): " + cache.get("user:1002"));
// 打印緩存的統(tǒng)計(jì)信息,如命中率、加載次數(shù)等
System.out.println("\n緩存統(tǒng)計(jì):");
System.out.println(cache.stats());
// 手動(dòng)移除指定key的緩存項(xiàng)
cache.invalidate("user:1001");
// 嘗試獲取已被移除的緩存項(xiàng),返回null表示不存在
System.out.println("\n移除后獲取: " + cache.getIfPresent("user:1001"));
} catch (Exception e) {
e.printStackTrace(); // 捕獲并打印異常信息
}
}
// 模擬從數(shù)據(jù)庫(kù)中根據(jù)key獲取數(shù)據(jù)的邏輯
private static String fetchDataFromDatabase(String key) {
// 打印當(dāng)前正在加載的key,用于觀察加載行為
System.out.println("正在從數(shù)據(jù)庫(kù)加載數(shù)據(jù): " + key);
try {
Thread.sleep(500); // 模擬數(shù)據(jù)庫(kù)查詢的延遲,增加真實(shí)感
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢復(fù)中斷狀態(tài)
}
// 返回模擬的數(shù)據(jù)庫(kù)查詢結(jié)果
return "Data for " + key;
}
}3、Caffeine
Caffeine采用了W-TinyLFU(LUR和LFU的優(yōu)點(diǎn)結(jié)合)開(kāi)源的緩存技術(shù)。緩存性能接近理論最優(yōu),屬于是Guava Cache的增強(qiáng)版。
Caffeine 是一個(gè)高性能的 Java 緩存庫(kù),它改進(jìn)了 Guava Cache 的設(shè)計(jì),具有以下特點(diǎn):
- 優(yōu)化的淘汰算法:采用 W-TinyLFU 算法,結(jié)合了 LRU 和 LFU 的優(yōu)點(diǎn)
- 卓越的性能:讀寫(xiě)性能接近理論最優(yōu)值
- 異步支持:提供異步加載和刷新機(jī)制
- 豐富的特性:支持多種淘汰策略、權(quán)重計(jì)算、統(tǒng)計(jì)等功能
- 內(nèi)存友好:相比 Guava Cache 減少約 50% 的內(nèi)存占用
<dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.9.3</version> </dependency>
package com.helloworld.demo;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
public class CaffeineCacheTest {
public static void main(String[] args) throws Exception {
// 創(chuàng)建一個(gè) Caffeine 緩存實(shí)例(注意:原注釋寫(xiě)的是 Guava Cache,實(shí)際使用的是 Caffeine)
Cache<String, String> loadingCache = Caffeine.newBuilder()
.initialCapacity(5) // 設(shè)置初始緩存容量為 5 個(gè)條目
.maximumSize(10) // 設(shè)置緩存最大容量為 10 個(gè)條目,超過(guò)時(shí)將按照策略淘汰
.expireAfterWrite(17, TimeUnit.SECONDS) // 寫(xiě)入后 17 秒過(guò)期
.expireAfterAccess(17, TimeUnit.SECONDS) // 最后一次訪問(wèn)后 17 秒過(guò)期
.build(); // 構(gòu)建緩存實(shí)例
String key = "key"; // 定義緩存的鍵
loadingCache.put(key, "這是測(cè)試方法"); // 手動(dòng)將鍵值對(duì)放入緩存
// 從緩存中獲取指定鍵的值
String value = loadingCache.getIfPresent(key);
System.out.println(" 從緩存中獲取指定鍵(key)的值:" + value); // 輸出:這是測(cè)試方法
// 將指定的鍵從緩存中移除(使其失效)
loadingCache.invalidate(key);
value = loadingCache.getIfPresent(key);
System.out.println(" 從緩存中獲取指定鍵(key)的值:" + value); // 輸出:null
}
}4、Encache
Ehcache是一個(gè)純java的進(jìn)程內(nèi)緩存框架,具有快速、精干的特點(diǎn)。是hibernate默認(rèn)的cacheprovider。
- 優(yōu)點(diǎn):支持多種緩存淘汰算法,包括LFU,LRU和FIFO;緩存支持堆內(nèi)緩存,堆外緩存和磁盤(pán)緩存;支持多種集群方案,解決數(shù)據(jù)共享問(wèn)題。
- 缺點(diǎn):性能比Caffeine差
<dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.10.8</version> </dependency>
package com.helloworld.demo;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
/**
* Ehcache 基礎(chǔ)使用示例類
*/
public class EhcacheBasicExample {
public static void main(String[] args) {
// 1. 創(chuàng)建緩存管理器(CacheManager),它是管理所有緩存的核心對(duì)象
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
// 初始化緩存管理器,使其可以開(kāi)始工作
cacheManager.init();
// 2. 創(chuàng)建一個(gè)緩存配置,定義緩存的鍵值類型和存儲(chǔ)策略
CacheConfigurationBuilder<String, String> config = CacheConfigurationBuilder
.newCacheConfigurationBuilder(
String.class, // 緩存鍵的類型為 String
String.class, // 緩存值的類型為 String
ResourcePoolsBuilder.heap(100) // 配置堆內(nèi)內(nèi)存緩存最多存儲(chǔ) 100 個(gè)條目
);
// 3. 根據(jù)配置創(chuàng)建一個(gè)名為 "myCache" 的緩存實(shí)例
Cache<String, String> myCache = cacheManager.createCache("myCache", config);
// 4. 使用緩存:存儲(chǔ)和讀取數(shù)據(jù)
myCache.put("key1", "value1"); // 往緩存中放入一個(gè)鍵值對(duì)
String value = myCache.get("key1"); // 從緩存中根據(jù) key 獲取對(duì)應(yīng)的 value
System.out.println("獲取的值: " + value); // 打印獲取到的緩存值
// 5. 使用完緩存后,關(guān)閉緩存管理器以釋放資源
cacheManager.close();
}
}到此這篇關(guān)于Java實(shí)現(xiàn)本地緩存的四種方法實(shí)現(xiàn)與對(duì)比的文章就介紹到這了,更多相關(guān)Java本地緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot集成Sentinel?組件實(shí)現(xiàn)基本限流功能(快速入門(mén))
Sentinel是阿里巴巴開(kāi)發(fā)的面向云原生微服務(wù)的高可用流控防護(hù)組件,支持流量控制、熔斷降級(jí)、系統(tǒng)負(fù)載保護(hù)等多維度防護(hù),適用于秒殺、消息削峰填谷等場(chǎng)景,本文給大家介紹Springboot集成Sentinel組件實(shí)現(xiàn)基本限流功能,感興趣的朋友跟隨小編一起看看吧2025-12-12
Java 信號(hào)量Semaphore的實(shí)現(xiàn)
這篇文章主要介紹了Java 信號(hào)量Semaphore的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
SpringBoot?@GroupSequenceProvider注解實(shí)現(xiàn)bean多屬性聯(lián)合校驗(yàn)的示例代碼
這篇文章主要介紹了SpringBoot?@GroupSequenceProvider注解實(shí)現(xiàn)bean多屬性聯(lián)合校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
Spring Boot開(kāi)發(fā)編譯后讀取不到@spring.profiles.active@的問(wèn)題及解決步驟
這篇文章主要介紹了Spring Boot開(kāi)發(fā)編譯后讀取不到@spring.profiles.active@的問(wèn)題及解決步驟,需要的朋友可以參考下2024-12-12
JAVA 實(shí)現(xiàn)延遲隊(duì)列的方法
這篇文章主要介紹了JAVA 實(shí)現(xiàn)延遲隊(duì)列的方法,文中講解非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
詳解Struts2動(dòng)態(tài)方法調(diào)用
這篇文章主要介紹了詳解Struts2動(dòng)態(tài)方法調(diào)用,涉及調(diào)用方法的代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-09-09

