Java 實(shí)現(xiàn)緩存的三種方式及問(wèn)題匯總
Java 實(shí)現(xiàn)緩存的三種方式
一、HashMap實(shí)現(xiàn)緩存
? 可以實(shí)現(xiàn)簡(jiǎn)單的本地緩存,但是實(shí)際開(kāi)發(fā)中不推薦,我們可以簡(jiǎn)單模擬一下緩存的實(shí)現(xiàn),
Step-1:實(shí)現(xiàn)一個(gè)緩存管理類(lèi)
public class LocalCache { public static HashMap<String,String> cache = new HashMap<>(); static { String name = UUID.randomUUID().toString(); LocalCache.cache.put(String.valueOf(1),name); System.out.println("id為1的數(shù)據(jù)添加到了緩存"); } } // 類(lèi)中有 `static` 修飾的靜態(tài)代碼塊,當(dāng)類(lèi)被加載的時(shí)候就會(huì)執(zhí)行,如有不懂的可以如下博客 // https://blog.csdn.net/weixin_62636014/article/details/136851287
Tips
:我們?cè)?code>static中完成了對(duì)緩存的初始化,你可以往緩存里面放入初始數(shù)據(jù)。
Step-2:將緩存管理類(lèi)交給 Spring 進(jìn)行管理
@Component public class LocalCache { public static HashMap<String,String> cache = new HashMap<>(); static { String name = UUID.randomUUID().toString(); LocalCache.cache.put(String.valueOf(1),name); System.out.println("id為1的數(shù)據(jù)添加到了緩存"); } @PostConstruct public void init() { String name = UUID.randomUUID().toString(); LocalCache.cache.put(String.valueOf(2),name); System.out.println("id為2的數(shù)據(jù)添加到了緩存"); } }
Tips
:在將緩存管理類(lèi)交給了Spring
進(jìn)行管理后,在方法上加入@PostConstruct
,可以使方法默認(rèn)執(zhí)行,注意該注解不是Spring
框架提供,僅僅是由Java JDK
提供的,主要是作用于Servlet
生命周期的注解,實(shí)現(xiàn)的是在Bean
初始化之前自定義操作
@PostConstruct
方法在 Bean
初始化中的執(zhí)行順序
Constructor
(構(gòu)造方法)@Autowired
(依賴(lài)注入)@PostConstruct
(注釋的初始化方法)
Step-3:編寫(xiě)接口測(cè)試緩存
@RequestMapping("test") public String test(Long id) { String name = LocalCache.cache.get(String.valueOf(id)); if (name != null) { System.out.println("緩存中存在,查詢(xún)緩存"); System.out.println(name); return name; } System.out.println("緩存中不存在,查詢(xún)數(shù)據(jù)庫(kù)"); // 查詢(xún)數(shù)據(jù)庫(kù)操作后,queryDataName方法沒(méi)有寫(xiě)了; // 大家可以自己配一下Mybatis和JDBC進(jìn)行數(shù)據(jù)庫(kù)查詢(xún),達(dá)到效果是從庫(kù)中查出來(lái) name; name = queryDataName(id); System.out.println(name); LocalCache.cache.put(String.valueOf(id),name); return name; } public String queryDataName(Long id) { String name = UUID.randomUUID().toString(); return name; }
Step-4:結(jié)果展示
? 這個(gè)是控制臺(tái)輸出,每個(gè)人的隨機(jī) UUID
不一致,我這個(gè)只是一個(gè)樣例
id為1的數(shù)據(jù)添加到了緩存
id為2的數(shù)據(jù)添加到了緩存
緩存中存在,查詢(xún)緩存
e2eadabe-3c42-4732-b465-e085ea5faf96
緩存中不存在,查詢(xún)數(shù)據(jù)庫(kù)
942ffe92-454f-4046-87e5-53e8b951d2a1
二、guava local cache 實(shí)現(xiàn)
Tips
:Guava
是Java
工具包,Guava Cache
是一套非常完善的本地緩存機(jī)制(JVM
緩存),工具類(lèi)就是封裝平常常用的方法,不需要你重復(fù)造輪子,節(jié)省開(kāi)發(fā)人員時(shí)間,我們一般需要知道怎么使用。其設(shè)計(jì)來(lái)源于CurrentHashMap
,可以按照多種策略來(lái)清理存儲(chǔ)在其中的緩存值且保持很高的并發(fā)讀寫(xiě)性能。
Guava
提供以下方面的能力
- 集合 [
collections
]- 緩存 [
caching
]- 原生類(lèi)型支持 [
primitives support
]- 并發(fā)庫(kù) [
concurrency libraries
]- 通用注解 [
common annotations
]- 字符串處理 [
string processing
]I/O
等等。
Step-1:導(dǎo)入guava 依賴(lài)
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> </dependency>
Step-2:使用guava創(chuàng)建簡(jiǎn)單緩存管理類(lèi)
為了方便展示,這里面使用了5
秒的緩存保留時(shí)間。
import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class GuavaLocalCache{ private Cache<String,String> fiveSecondCache = CacheBuilder.newBuilder() //設(shè)置緩存初始大小,應(yīng)該合理設(shè)置,后續(xù)會(huì)擴(kuò)容 .initalCapacity(10) //最大值 .maximumSize(100) //并發(fā)數(shù)設(shè)置 .concurrencyLevel(5) //緩存過(guò)期時(shí)間,寫(xiě)入后5秒鐘過(guò)期 .expireAfterWrite(5,TimeUnit.SECONDS) //統(tǒng)計(jì)緩存命中率 .recordStats() .build() public Cache<String,String> getFiveSecondCache() { return fiveSecondCache; } // 這里就是拿到緩存對(duì)象。 public void setFiveSecondCache(Cache<String,String> fiveSecondCache) { this.fiveSecondCache = fiveSecondCache; } }
Step-3:使用 guava cache,并嘗試統(tǒng)計(jì)命中率
public class test { @Autowired private GuavaLocalCache guavaLocalCache; @RequestMapping("guavaTest") public String guavaTest(Long id) { // 獲取緩存 Cache<String,String> fiveSecondCache = guavaLocalCache.getFiveSecondCache(); // 從緩存中獲取對(duì)象 String nameCache = fiveSecondCache.getIfPresent(String.valueOf(id)); // 緩存中存在 if (nameCache != null) { System.out.println("緩存命中:"+ nameCache + "," + getCacheStats(fiveSecondCache)); return nameCache; } // 將數(shù)據(jù)存入緩存 System.out.println("緩存未命中," + getCacheStats(fiveSecondCache)); nameCache = id + "-" + UUID.randomUUID().toString(); fiveSecondCache.put(String.valueOf(id),nameCache); return nameCache; } public String getCacheStats(Cache<String,String> cahce) { CacheStats stats = cache.stats(); return "緩沖命中率:"+stats.hitRate() +" 被清除緩沖數(shù):" + stats.evictionCount(); } }
三、使用redis實(shí)現(xiàn)緩存
Tips
:Redis
(全稱(chēng):Remote Dictionary Server
遠(yuǎn)程字典服務(wù))是一個(gè)開(kāi)源的使用ANSI C
語(yǔ)言 編寫(xiě)、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value
數(shù)據(jù)庫(kù) 。Redis
一般被用來(lái)做緩存用的,它實(shí)際上也是一種數(shù)據(jù)庫(kù)(非關(guān)系型數(shù)據(jù)庫(kù)),可以對(duì)經(jīng)常使用到的數(shù)據(jù)進(jìn)行存儲(chǔ),也就是大家所說(shuō)的緩存。官方給出的數(shù)據(jù)是,Redis
能達(dá)到10w+
的QPS
( 每秒查詢(xún)速度 ) 。
Tips
: 為什么Redis
的速度比Mysql
等這種數(shù)據(jù)快呢?? 因?yàn)?
Redis
存儲(chǔ)的是key-values
格式的數(shù)據(jù),時(shí)間復(fù)雜度是O(1)
,即直接通過(guò)key
查詢(xún)對(duì)應(yīng)的value
。 而如Mysql
數(shù)據(jù)庫(kù),底層的實(shí)現(xiàn)是B+
樹(shù),時(shí)間復(fù)雜度是O(logn)
。? 最重要的一點(diǎn)是,數(shù)據(jù)庫(kù)的數(shù)據(jù)是存儲(chǔ)在磁盤(pán)中的,而
Redis
是存儲(chǔ)在內(nèi)存當(dāng)中的,它們的速度差距不言而喻。但Redis
也支持持久化存儲(chǔ)
Step-1:導(dǎo)入Redis 依賴(lài)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
在 SpringBoot
配置文件中加入設(shè)置,我使用的是yml
形式的文件,如果沒(méi)有密碼的話不填就好了
redis: # IP地址 host: XXX.XXX.XXX.XXX # 密碼 password: XXXXXXXX # 端口,默認(rèn)為6379 port: 6379 # 數(shù)據(jù)庫(kù)索引 database: 0 # 連接超時(shí)時(shí)間 timeout: 10s lettuce: pool: # 連接池中的最小空閑連接 min-idle: 1 # 連接池中的最大空閑連接 max-idle: 8 # 連接池的最大數(shù)據(jù)庫(kù)連接數(shù) max-active: 8 # #連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制) max-wait: -1ms
Step-2:編寫(xiě)測(cè)試接口
public class TestRedis{ // 下面StringRedisTemplate 是一個(gè)繼承自 RedisTemplate的類(lèi) @Autowired private StringRedisTemplate stringRedisTemplate; @RequestMapping("/redisTest") public String redisCacheTest(Long id){ String name = stringRedisTemplate.opsForValue().get(String.valueOf(id)); if (name != null){ System.out.println("緩存中存在,查詢(xún)緩存"); System.out.println(name); return name; } System.out.println("緩存中不存在,查詢(xún)數(shù)據(jù)庫(kù)"); name = id + "-" + UUID.randomUUID().toString(); System.out.println(name); stringRedisTemplate.opsForValue().set(String.valueOf(id),name); return name; } }
Step-3:進(jìn)行接口測(cè)試,并使用Redis DeskTop Manager 進(jìn)行查看
參考文章
- 【java緩存、redis緩存、guava緩存】java中實(shí)現(xiàn)緩存的幾種方式_java緩存cache-CSDN博客
- 一篇文章搞定 Redis 基礎(chǔ)知識(shí) - 知乎 (zhihu.com)
- Java本地緩存技術(shù)選型(Guava Cache、Caffeine、Encache) - 掘金 (juejin.cn)
- MemCache原理超詳細(xì)解讀(僅學(xué)習(xí)) - 知乎 (zhihu.com)
- PostConstruct注解詳細(xì)使用說(shuō)明及理解-CSDN博客
- PostConstruct (Java Platform SE 8 ) (oracle.com)
- Java開(kāi)發(fā)利器Guava Cache之使用篇 - 掘金 (juejin.cn)
- Google guava 工具類(lèi)的介紹和使用 - 掘金 (juejin.cn)
- Redis詳細(xì)介紹(精簡(jiǎn)版)_redis 服務(wù) 精簡(jiǎn)-CSDN博客
- 初識(shí)Redis,看這一篇就夠了
到此這篇關(guān)于Java 實(shí)現(xiàn)緩存的三種方式問(wèn)題匯總的文章就介紹到這了,更多相關(guān)Java 實(shí)現(xiàn)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 如何基于LoadingCache實(shí)現(xiàn)Java本地緩存
- Java緩存Map設(shè)置過(guò)期時(shí)間實(shí)現(xiàn)解析
- 如何在 Java 中實(shí)現(xiàn)一個(gè) redis 緩存服務(wù)
- Java中LocalCache本地緩存實(shí)現(xiàn)代碼
- Java LocalCache 本地緩存的實(shí)現(xiàn)實(shí)例
- Java本地緩存的實(shí)現(xiàn)代碼
- Java自定義注解實(shí)現(xiàn)Redis自動(dòng)緩存的方法
- Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的緩存方法
- java shiro實(shí)現(xiàn)退出登陸清空緩存
相關(guān)文章
Java類(lèi)和對(duì)象習(xí)題及詳細(xì)答案解析
這篇文章主要介紹了Java類(lèi)和對(duì)象的相關(guān)知識(shí),包括局部變量初始化、靜態(tài)方法、靜態(tài)導(dǎo)入、構(gòu)造方法、代碼塊執(zhí)行順序、toString方法重寫(xiě)、類(lèi)變量和靜態(tài)成員變量的訪問(wèn)等,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02Java高效實(shí)現(xiàn)excel轉(zhuǎn)pdf(支持帶圖片的轉(zhuǎn)換)
這篇文章主要為大家詳細(xì)介紹了如何用java實(shí)現(xiàn)excel轉(zhuǎn)pdf文件,并且支持excel單元格中帶有圖片的轉(zhuǎn)換,文中的示例代碼講解詳細(xì),需要的可以參考下2024-01-01基于Redisson實(shí)現(xiàn)注解式分布式鎖的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何基于Redisson實(shí)現(xiàn)注解式分布式鎖,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下2023-07-07如何獲取MyBatis Plus執(zhí)行的完整的SQL語(yǔ)句
這篇文章主要介紹了如何獲取MyBatis Plus執(zhí)行的完整的SQL語(yǔ)句問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07Spring動(dòng)態(tài)管理定時(shí)任務(wù)之ThreadPoolTaskScheduler解讀
這篇文章主要介紹了Spring動(dòng)態(tài)管理定時(shí)任務(wù)之ThreadPoolTaskScheduler解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Spring Boot 項(xiàng)目創(chuàng)建的詳細(xì)步驟(圖文)
這篇文章主要介紹了Spring Boot 項(xiàng)目創(chuàng)建的詳細(xì)步驟(圖文),這里我們有兩種創(chuàng)建Spring Boot項(xiàng)目的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05SpringBoot單點(diǎn)登錄實(shí)現(xiàn)過(guò)程詳細(xì)分析
這篇文章主要介紹了SpringBoot單點(diǎn)登錄實(shí)現(xiàn)過(guò)程,單點(diǎn)登錄英文全稱(chēng)Single?Sign?On,簡(jiǎn)稱(chēng)就是SSO。它的解釋是:在多個(gè)應(yīng)用系統(tǒng)中,只需要登錄一次,就可以訪問(wèn)其他相互信任的應(yīng)用系統(tǒng)2022-12-12