Java實現(xiàn)本地緩存的方式匯總
一、概念
引入緩存,主要用于實現(xiàn)系統(tǒng)的高性能,高并發(fā)。將數(shù)據(jù)庫查詢出來的數(shù)據(jù)放入緩存服務中,因為緩存是存儲在內(nèi)存中的,內(nèi)存的讀寫性能遠超磁盤的讀寫性能,所以訪問的速度非??臁5请娔X重啟后,內(nèi)存中的數(shù)據(jù)會全部清除,而磁盤中的數(shù)據(jù)雖然讀寫性能很差,但是數(shù)據(jù)不會丟失。
二、手寫本地緩存
首先創(chuàng)建一個緩存實體類
package com.example.vuespringboot.bean; import lombok.Data; /** * @author qx * @date 2023/7/27 * @des 自定義緩存實體類 */ @Data public class MyCache { /** * 鍵 */ private String key; /** * 值 */ private Object value; /** * 過期時間 */ private Long expireTime; }
接著我們編寫一個緩存操作的工具類
package com.example.vuespringboot.util; import com.example.vuespringboot.bean.MyCache; import java.time.Duration; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * @author qx * @date 2023/7/27 * @des 自定義本地緩存工具類 */ public class CacheUtil { /** * 緩存數(shù)據(jù)Map */ private static final Map<String, MyCache> CACHE_MAP = new ConcurrentHashMap<>(); /** * 定時器線程池,用于清除過期緩存 */ private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); static { // 注冊一個定時線程任務,服務啟動1秒之后,每隔500毫秒執(zhí)行一次 // 定時清理過期緩存 executorService.scheduleAtFixedRate(CacheUtil::clearCache, 1000, 500, TimeUnit.MILLISECONDS); } /** * 添加緩存 * * @param key 緩存鍵 * @param value 緩存值 * @param expire 過期時間,單位秒 */ public static void put(String key, Object value, long expire) { MyCache myCache = new MyCache(); myCache.setKey(key); myCache.setValue(value); if (expire > 0) { long expireTime = System.currentTimeMillis() + Duration.ofSeconds(expire).toMillis(); myCache.setExpireTime(expireTime); } CACHE_MAP.put(key, myCache); } /** * 獲取緩存 * * @param key 緩存鍵 * @return 緩存數(shù)據(jù) */ public static Object get(String key) { if (CACHE_MAP.containsKey(key)) { return CACHE_MAP.get(key).getValue(); } return null; } /** * 移除緩存 * * @param key 緩存鍵 */ public static void remove(String key) { CACHE_MAP.remove(key); } /** * 清理過期的緩存數(shù)據(jù) */ private static void clearCache() { if (CACHE_MAP.size() <= 0) { return; } // 判斷是否過期 過期就從緩存Map刪除這個元素 CACHE_MAP.entrySet().removeIf(entry -> entry.getValue().getExpireTime() != null && entry.getValue().getExpireTime() > System.currentTimeMillis()); } }
最后,我們來測試一下緩存服務
package com.example.vuespringboot; import com.example.vuespringboot.util.CacheUtil; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.util.concurrent.TimeUnit; @SpringBootTest class VueSpringBootApplicationTests { @Test void contextLoads() throws InterruptedException { // 寫入緩存數(shù)據(jù) 2秒后過期 CacheUtil.put("name", "qx", 2); Object value1 = CacheUtil.get("name"); System.out.println("第一次查詢結果:" + value1); // 停頓3秒 TimeUnit.SECONDS.sleep(3); Object value2 = CacheUtil.get("name"); System.out.println("第二次查詢結果:" + value2); } }
啟動測試,我們從控制臺的返回看到輸出結果和我們的預期一致!
第一次查詢結果:qx
第二次查詢結果:null
實現(xiàn)思路其實很簡單,采用ConcurrentHashMap
作為緩存數(shù)據(jù)存儲服務,然后開啟一個定時調(diào)度,每隔500
毫秒檢查一下過期的緩存數(shù)據(jù),然后清除掉!
三、基于Guava Cache實現(xiàn)本地緩存
相比自己編寫的緩存服務,Guava Cache 要強大的多,支持很多特性如下:
- 支持最大容量限制
- 支持兩種過期刪除策略(插入時間和讀取時間)
- 支持簡單的統(tǒng)計功能
- 基于 LRU 算法實現(xiàn)
1.添加gugva的依賴
<!--guava--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency>
2.測試
@Test void testGuava() throws ExecutionException, InterruptedException { // 創(chuàng)建一個緩存實例 Cache<String, String> cache = CacheBuilder.newBuilder() // 初始容量 .initialCapacity(5) // 最大緩存數(shù),超出淘汰 .maximumSize(10) // 過期時間 設置寫入3秒后過期 .expireAfterWrite(3, TimeUnit.SECONDS) .build(); // 寫入緩存數(shù)據(jù) cache.put("name", "qq"); // 讀取緩存數(shù)據(jù) String value1 = cache.get("name", () -> "key過期"); System.out.println("第一次查詢結果:" + value1); // 停頓4秒 TimeUnit.SECONDS.sleep(4); // 讀取緩存數(shù)據(jù) String value2 = cache.get("name", () -> "key過期"); System.out.println("第二次查詢結果:" + value2); }
啟動測試,我們從控制臺的返回看到輸出結果和我們的預期一致!
第一次查詢結果:qq
第二次查詢結果:key過期
四、基于 Caffeine 實現(xiàn)本地緩存
Caffeine 是基于 java8 實現(xiàn)的新一代緩存工具,緩存性能接近理論最優(yōu),可以看作是 Guava Cache 的增強版,功能上兩者類似,不同的是 Caffeine 采用了一種結合 LRU、LFU 優(yōu)點的算法:W-TinyLFU,在性能上有明顯的優(yōu)越性。
1.引入Caffeine
<!--caffeine--> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.9.3</version> </dependency>
2.測試
@Test void testCaffeine() throws InterruptedException { // 創(chuàng)建一個緩存實例 Cache<String, String> cache = Caffeine.newBuilder() // 初始容量 .initialCapacity(5) // 最大緩存數(shù),超出淘汰 .maximumSize(10) // 設置緩存寫入間隔多久過期 .expireAfterWrite(3, TimeUnit.SECONDS) // 設置緩存最后訪問后間隔多久淘汰,實際很少用到 .build(); // 寫入緩存數(shù)據(jù) cache.put("userName", "張三"); // 讀取緩存數(shù)據(jù) String value1 = cache.get("userName", (key) -> { // 如果key不存在,會執(zhí)行回調(diào)方法 return "key已過期"; }); System.out.println("第一次查詢結果:" + value1); // 停頓4秒 Thread.sleep(4000); // 讀取緩存數(shù)據(jù) String value2 = cache.get("userName", (key) -> { // 如果key不存在,會執(zhí)行回調(diào)方法 return "key已過期"; }); System.out.println("第二次查詢結果:" + value2); }
輸出結果:
第一次查詢結果:張三
第二次查詢結果:key已過期
五、基于 Encache 實現(xiàn)本地緩存
1.引入ehcache依賴
<!--ehcache--> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.9.7</version> </dependency>
2.自定義過期策略實現(xiàn)
package com.example.vuespringboot.util; import org.ehcache.expiry.ExpiryPolicy; import java.time.Duration; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; /** * @author qx * @date 2023/7/27 * @des 自定義過期策略實現(xiàn) */ public class CustomExpiryPolicy<K, V> implements ExpiryPolicy<K, V> { private final Map<K, Duration> keyExpireMap = new ConcurrentHashMap(); public Duration setExpire(K key, Duration duration) { return keyExpireMap.put(key, duration); } public Duration getExpireByKey(K key) { return Optional.ofNullable(keyExpireMap.get(key)) .orElse(null); } public Duration removeExpire(K key) { return keyExpireMap.remove(key); } @Override public Duration getExpiryForCreation(K key, V value) { return Optional.ofNullable(getExpireByKey(key)) .orElse(Duration.ofNanos(Long.MAX_VALUE)); } @Override public Duration getExpiryForAccess(K key, Supplier<? extends V> value) { return getExpireByKey(key); } @Override public Duration getExpiryForUpdate(K key, Supplier<? extends V> oldValue, V newValue) { return getExpireByKey(key); } }
3.測試
package com.example.vuespringboot.util; 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 java.time.Duration; /** * @author qx * @date 2023/7/27 * @des 測試Encache */ public class EncacheTest { public static void main(String[] args) throws InterruptedException { String userCache = "userCache"; // 自定義過期策略 CustomExpiryPolicy<Object, Object> customExpiryPolicy = new CustomExpiryPolicy<>(); // 聲明一個容量為20的堆內(nèi)緩存配置 CacheConfigurationBuilder configurationBuilder = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(20)) .withExpiry(customExpiryPolicy); // 初始化一個緩存管理器 CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() // 創(chuàng)建cache實例 .withCache(userCache, configurationBuilder) .build(true); // 獲取cache實例 Cache<String, String> cache = cacheManager.getCache(userCache, String.class, String.class); // 獲取過期策略 CustomExpiryPolicy expiryPolicy = (CustomExpiryPolicy) cache.getRuntimeConfiguration().getExpiryPolicy(); // 寫入緩存數(shù)據(jù) cache.put("userName", "張三"); // 設置3秒過期 expiryPolicy.setExpire("userName", Duration.ofSeconds(3)); // 讀取緩存數(shù)據(jù) String value1 = cache.get("userName"); System.out.println("第一次查詢結果:" + value1); // 停頓4秒 Thread.sleep(4000); // 讀取緩存數(shù)據(jù) String value2 = cache.get("userName"); System.out.println("第二次查詢結果:" + value2); } }
輸出結果:
第一次查詢結果:張三
第二次查詢結果:null
到此這篇關于Java實現(xiàn)本地緩存的幾種方式的文章就介紹到這了,更多相關java本地緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在Android的應用中實現(xiàn)網(wǎng)絡圖片異步加載的方法
這篇文章主要介紹了在Android的應用中實現(xiàn)網(wǎng)絡圖片異步加載的方法,一定程度上有助于提高安卓程序的使用體驗,需要的朋友可以參考下2015-07-07JAVA實現(xiàn)社會統(tǒng)一信用代碼校驗的方法
這篇文章主要介紹了JAVA實現(xiàn)社會統(tǒng)一信用代碼校驗的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-07-07使用Filter過濾器中訪問getSession()要轉(zhuǎn)化
這篇文章主要介紹了使用Filter過濾器中訪問getSession()要轉(zhuǎn)化,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01詳解JavaEE 使用 Redis 數(shù)據(jù)庫進行內(nèi)容緩存和高訪問負載
本篇文章主要介紹了JavaEE 使用 Redis 數(shù)據(jù)庫進行內(nèi)容緩存和高訪問負載,具有一定的參考價值,有興趣的可以了解一下2017-08-08