Java實現(xiàn)本地緩存的四種方法實現(xiàn)與對比
本地緩存比如 caffine,guava cache 這些都是比較常用的,本地緩存的優(yōu)點就是速度非常快,沒有網(wǎng)絡(luò)消耗,缺點就是應(yīng)用重啟后,緩存就會丟失。
Java緩存技術(shù)可分為遠端緩存和本地緩存,遠端緩存常用的方案有著名的redis,而本地緩存的代表技術(shù)主要有HashMap,Guava Cache,Caffeine和Encahche。
1、HashMap
通過Map的底層方式,直接將需要緩存的對象放在內(nèi)存中。
- 優(yōu)點:簡單粗暴,不需要引入第三方包,比較適合一些比較簡單的場景。
- 缺點:沒有緩存淘汰策略,定制化開發(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;
/**
* 定義一個基于LinkedHashMap實現(xiàn)的線程安全的LRU緩存類
*/
public class LRUCache extends LinkedHashMap<Object, Object> { // 明確泛型類型為<Object, Object>,提高代碼可讀性
/**
* 可重入讀寫鎖,用于保證多線程環(huán)境下對緩存的并發(fā)讀寫操作的安全性
*/
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock(); // 讀鎖,用于并發(fā)讀取時共享訪問
private Lock writeLock = readWriteLock.writeLock(); // 寫鎖,用于寫入或修改時獨占訪問
/**
* 緩存的最大容量限制,超過此容量將移除最久未使用的條目
*/
private int maxSize;
/**
* 構(gòu)造函數(shù),初始化LRU緩存并設(shè)置最大容量
*
* @param maxSize 緩存允許存儲的最大條目數(shù)
*/
public LRUCache(int maxSize) {
// 調(diào)用父類LinkedHashMap的構(gòu)造方法:
// 參數(shù)1:初始容量為maxSize + 1,避免頻繁擴容
// 參數(shù)2:負載因子為1.0f,表示哈希表填滿到100%時才擴容
// 參數(shù)3:accessOrder為true,表示按照訪問順序排序,實現(xiàn)LRU策略
super(maxSize + 1, 1.0f, true);
this.maxSize = maxSize;
}
/**
* 重寫get方法,獲取指定key對應(yīng)的value,使用讀鎖保證線程安全
*
* @param key 要查找的鍵
* @return 對應(yīng)的值,如果不存在則返回null
*/
@Override
public Object get(Object key) {
readLock.lock(); // 加讀鎖,允許多個線程同時讀
try {
return super.get(key); // 調(diào)用父類的get方法
} finally {
readLock.unlock(); // 確保讀鎖最終被釋放
}
}
/**
* 重寫put方法,向緩存中添加或更新鍵值對,使用寫鎖保證線程安全
*
* @param key 要插入或更新的鍵
* @param value 要插入或更新的值
* @return 之前與key關(guān)聯(lián)的值,如果沒有則返回null
*/
@Override
public Object put(Object key, Object value) {
writeLock.lock(); // 加寫鎖,確保同一時間只有一個線程可以寫
try {
return super.put(key, value); // 調(diào)用父類的put方法
} finally {
writeLock.unlock(); // 確保寫鎖最終被釋放
}
}
/**
* 重寫removeEldestEntry方法,當緩存大小超過maxSize時,移除最久未使用的條目
*
* @param eldest 最久未訪問的鍵值對Entry
* @return 如果當前緩存大小超過最大容量,則返回true,觸發(fā)刪除最老的條目;否則返回false
*/
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > maxSize; // 判斷當前緩存大小是否超出限制
}
// 測試主方法
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 庫中的一個本地緩存實現(xiàn),它提供了以下主要特性:
- 自動加載:當緩存未命中時自動從指定來源加載數(shù)據(jù)
- 多種淘汰策略:支持基于大小、時間和引用的淘汰
- 統(tǒng)計功能:內(nèi)置緩存命中率統(tǒng)計
- 線程安全:內(nèi)置并發(fā)控制機制
- 監(jiān)聽器:支持緩存移除通知
<!-- 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)建一個Guava的LoadingCache實例,支持自動加載緩存項
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100) // 設(shè)置緩存最大容量為100個條目
.expireAfterWrite(10, TimeUnit.MINUTES) // 設(shè)置緩存項在寫入10分鐘后自動過期
.recordStats() // 開啟緩存統(tǒng)計功能,可以獲取命中率等信息
.build(new CacheLoader<String, String>() { // 定義緩存未命中時的加載邏輯
@Override
public String load(String key) throws Exception {
// 當根據(jù)key獲取不到緩存值時,調(diào)用此方法從數(shù)據(jù)源(如數(shù)據(jù)庫)加載數(shù)據(jù)
return fetchDataFromDatabase(key);
}
});
try {
// 第一次獲取key為"user:1001"的值,由于緩存中沒有,會觸發(fā)load方法從數(shù)據(jù)庫加載
System.out.println("第一次獲取(從數(shù)據(jù)庫加載): " + cache.get("user:1001"));
// 第二次獲取相同的key,此時緩存中已有該值,直接從緩存返回,不會再次加載
System.out.println("第二次獲取(從緩存獲取): " + cache.get("user:1001"));
// 手動向緩存中放入一個鍵值對,繞過自動加載邏輯
cache.put("user:1002", "Manual Data");
// 獲取手動放入的緩存值
System.out.println("手動放入的數(shù)據(jù): " + cache.get("user:1002"));
// 打印緩存的統(tǒng)計信息,如命中率、加載次數(shù)等
System.out.println("\n緩存統(tǒng)計:");
System.out.println(cache.stats());
// 手動移除指定key的緩存項
cache.invalidate("user:1001");
// 嘗試獲取已被移除的緩存項,返回null表示不存在
System.out.println("\n移除后獲取: " + cache.getIfPresent("user:1001"));
} catch (Exception e) {
e.printStackTrace(); // 捕獲并打印異常信息
}
}
// 模擬從數(shù)據(jù)庫中根據(jù)key獲取數(shù)據(jù)的邏輯
private static String fetchDataFromDatabase(String key) {
// 打印當前正在加載的key,用于觀察加載行為
System.out.println("正在從數(shù)據(jù)庫加載數(shù)據(jù): " + key);
try {
Thread.sleep(500); // 模擬數(shù)據(jù)庫查詢的延遲,增加真實感
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢復(fù)中斷狀態(tài)
}
// 返回模擬的數(shù)據(jù)庫查詢結(jié)果
return "Data for " + key;
}
}3、Caffeine
Caffeine采用了W-TinyLFU(LUR和LFU的優(yōu)點結(jié)合)開源的緩存技術(shù)。緩存性能接近理論最優(yōu),屬于是Guava Cache的增強版。
Caffeine 是一個高性能的 Java 緩存庫,它改進了 Guava Cache 的設(shè)計,具有以下特點:
- 優(yōu)化的淘汰算法:采用 W-TinyLFU 算法,結(jié)合了 LRU 和 LFU 的優(yōu)點
- 卓越的性能:讀寫性能接近理論最優(yōu)值
- 異步支持:提供異步加載和刷新機制
- 豐富的特性:支持多種淘汰策略、權(quán)重計算、統(tǒng)計等功能
- 內(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)建一個 Caffeine 緩存實例(注意:原注釋寫的是 Guava Cache,實際使用的是 Caffeine)
Cache<String, String> loadingCache = Caffeine.newBuilder()
.initialCapacity(5) // 設(shè)置初始緩存容量為 5 個條目
.maximumSize(10) // 設(shè)置緩存最大容量為 10 個條目,超過時將按照策略淘汰
.expireAfterWrite(17, TimeUnit.SECONDS) // 寫入后 17 秒過期
.expireAfterAccess(17, TimeUnit.SECONDS) // 最后一次訪問后 17 秒過期
.build(); // 構(gòu)建緩存實例
String key = "key"; // 定義緩存的鍵
loadingCache.put(key, "這是測試方法"); // 手動將鍵值對放入緩存
// 從緩存中獲取指定鍵的值
String value = loadingCache.getIfPresent(key);
System.out.println(" 從緩存中獲取指定鍵(key)的值:" + value); // 輸出:這是測試方法
// 將指定的鍵從緩存中移除(使其失效)
loadingCache.invalidate(key);
value = loadingCache.getIfPresent(key);
System.out.println(" 從緩存中獲取指定鍵(key)的值:" + value); // 輸出:null
}
}4、Encache
Ehcache是一個純java的進程內(nèi)緩存框架,具有快速、精干的特點。是hibernate默認的cacheprovider。
- 優(yōu)點:支持多種緩存淘汰算法,包括LFU,LRU和FIFO;緩存支持堆內(nèi)緩存,堆外緩存和磁盤緩存;支持多種集群方案,解決數(shù)據(jù)共享問題。
- 缺點:性能比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),它是管理所有緩存的核心對象
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
// 初始化緩存管理器,使其可以開始工作
cacheManager.init();
// 2. 創(chuàng)建一個緩存配置,定義緩存的鍵值類型和存儲策略
CacheConfigurationBuilder<String, String> config = CacheConfigurationBuilder
.newCacheConfigurationBuilder(
String.class, // 緩存鍵的類型為 String
String.class, // 緩存值的類型為 String
ResourcePoolsBuilder.heap(100) // 配置堆內(nèi)內(nèi)存緩存最多存儲 100 個條目
);
// 3. 根據(jù)配置創(chuàng)建一個名為 "myCache" 的緩存實例
Cache<String, String> myCache = cacheManager.createCache("myCache", config);
// 4. 使用緩存:存儲和讀取數(shù)據(jù)
myCache.put("key1", "value1"); // 往緩存中放入一個鍵值對
String value = myCache.get("key1"); // 從緩存中根據(jù) key 獲取對應(yīng)的 value
System.out.println("獲取的值: " + value); // 打印獲取到的緩存值
// 5. 使用完緩存后,關(guān)閉緩存管理器以釋放資源
cacheManager.close();
}
}到此這篇關(guān)于Java實現(xiàn)本地緩存的四種方法實現(xiàn)與對比的文章就介紹到這了,更多相關(guān)Java本地緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis-generator生成多次重復(fù)代碼問題以及解決
在使用MySQL數(shù)據(jù)庫時,如果多個數(shù)據(jù)庫中存在相同表名,即使在URL中配置了數(shù)據(jù)庫名,也可能導(dǎo)致數(shù)據(jù)互相影響,解決這一問題的方法是在mapper-generator-config.xml文件中添加catalog屬性,明確指定逆向工程代碼所涉及表的數(shù)據(jù)庫名2024-10-10
Java線程之join_動力節(jié)點Java學(xué)院整理
join() 定義在Thread.java中,下文通過源碼分享join(),需要的朋友參考下吧2017-05-05
Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程
Hibernate可以將Java中幾個內(nèi)置的集合結(jié)構(gòu)映射為數(shù)據(jù)庫使用的關(guān)系模型,下面我們就來看一下Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程:2016-07-07
Java數(shù)據(jù)結(jié)構(gòu)優(yōu)先隊列實練
通常都把隊列比喻成排隊買東西,大家都很守秩序,先排隊的人就先買東西。但是優(yōu)先隊列有所不同,它不遵循先進先出的規(guī)則,而是根據(jù)隊列中元素的優(yōu)先權(quán),優(yōu)先權(quán)最大的先被取出,這篇文章主要介紹了java優(yōu)先隊列的真題,感興趣的朋友一起看看吧2022-07-07
Java中防止數(shù)據(jù)重復(fù)提交超簡單的6種方法
在平時開發(fā)中,如果網(wǎng)速比較慢的情況下,用戶提交表單后,發(fā)現(xiàn)服務(wù)器半天都沒有響應(yīng),那么用戶可能會以為是自己沒有提交表單,就會再點擊提交按鈕重復(fù)提交表單,這篇文章主要給大家介紹了關(guān)于Java中防止數(shù)據(jù)重復(fù)提交超簡單的6種方法,需要的朋友可以參考下2021-11-11
Java實現(xiàn)的數(shù)字簽名算法RSA完整示例
這篇文章主要介紹了Java實現(xiàn)的數(shù)字簽名算法RSA,結(jié)合完整實例形式詳細分析了RSA算法的相關(guān)概念、原理、實現(xiàn)方法及操作技巧,需要的朋友可以參考下2019-09-09

