java使用軟引用實(shí)現(xiàn)緩存機(jī)制示例
正文
“讀多寫(xiě)少”是大部分項(xiàng)目的一個(gè)特點(diǎn)。例如“購(gòu)物”,總是看的人多(讀)、買(mǎi)的人少(寫(xiě))。因此,如果能減少“讀”請(qǐng)求的次數(shù),就能減少服務(wù)端的壓力。最直接的減少“讀”請(qǐng)求次數(shù)的方法就是使用緩存。
軟引用和強(qiáng)引用
對(duì)于同一個(gè)讀請(qǐng)求,只需要在第一次訪問(wèn)時(shí)從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù),并將查詢到的數(shù)據(jù)保存到緩存中,之后的查詢請(qǐng)求就可以直接在緩存中獲取,從而減少對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)次數(shù)。
這種情況我們生活種經(jīng)常會(huì)看到,比如訪問(wèn)某app某商品,第一次進(jìn)去會(huì)加載一會(huì)會(huì),后面繼續(xù)點(diǎn)擊是直接出現(xiàn)。
根據(jù)目前所學(xué)知識(shí),我們可以使用 HashMap 在內(nèi)存級(jí)別實(shí)現(xiàn)緩存功能。
例如,可以使用一個(gè) HashMap 對(duì)象保存客戶端第一次請(qǐng)求的結(jié)果,之后,當(dāng)客戶端再次發(fā)起讀請(qǐng)求時(shí),就從 HashMap 對(duì)象中遍歷查詢,如果 HashMap 中已經(jīng)保存過(guò)客戶要查詢的數(shù)據(jù),就直接返回,否則再向數(shù)據(jù)庫(kù)發(fā)起查詢請(qǐng)求,并將查詢結(jié)果保存到 HashMap 中。
這種緩存的設(shè)計(jì)思路十分簡(jiǎn)單,但也存在一個(gè)問(wèn)題:HashMap 中緩存的數(shù)據(jù)何時(shí)被清空?
內(nèi)存容量是有限制的,如果永無(wú)止盡的向 HashMap 緩存數(shù)據(jù),顯然會(huì)對(duì)內(nèi)存容量帶來(lái)壓力。一種解決方案就是使用 JVM 提供的軟引用,實(shí)現(xiàn)對(duì) HashMap 中緩存數(shù)據(jù)的淘汰策略。
開(kāi)發(fā)中最常使用的是強(qiáng)引用,例如 Goods goods = new Goods()
就創(chuàng)建了一個(gè)強(qiáng)引用對(duì)象“goods”。只要強(qiáng)引用的作用域沒(méi)有結(jié)束,或者沒(méi)有被開(kāi)發(fā)者手工設(shè)置為 null,那么強(qiáng)引用對(duì)象會(huì)始終存在于 JVM 內(nèi)存中。
而 JVM 提供的軟引用就比較靈活:當(dāng) JVM 的內(nèi)存足夠時(shí),GC 對(duì)待軟引用和強(qiáng)引用的方式是一樣的;但當(dāng) JVM 的內(nèi)存不足時(shí),GC 就會(huì)去主動(dòng)回收軟引用對(duì)象。
可見(jiàn),非常適合將緩存的對(duì)象存放在軟引用中。軟引用需要借助 JDK 提供的 java.lang.ref.SoftReference
類來(lái)實(shí)現(xiàn)。
項(xiàng)目
使用idea創(chuàng)建一個(gè)maven項(xiàng)目
結(jié)構(gòu)如下
首先對(duì)Good實(shí)體類進(jìn)行編寫(xiě)。
要求,goods有屬性id,name并書(shū)寫(xiě)他的getset方法,以及有參無(wú)參構(gòu)造器。
這里代碼省略。
然后我們?cè)趃oodbase里面編寫(xiě)代碼,模擬一個(gè)數(shù)據(jù)庫(kù)
里面主要有hashmap,并且通過(guò)get方法,得到該hashmap
public class GoodsBase { private static Map<String, Goods> base = new HashMap<>(); public static Map<String, Goods> getBase() { return base; } }
然后書(shū)寫(xiě)goodscache緩存類
這里我們需要接觸一個(gè)新關(guān)鍵字volatile
- 使用volatile關(guān)鍵字會(huì)強(qiáng)制將修改的值立即寫(xiě)入主存;
- 使用volatile關(guān)鍵字的話,當(dāng)主線程修改時(shí),會(huì)導(dǎo)致RunThread的工作內(nèi)存中isRunning變量的緩存值變得無(wú)效。
- 由于RunThread的工作內(nèi)存中緩存變量isRunning緩存無(wú)效,所以會(huì)再次從主存中讀取isRunning變量值。
在map里面通過(guò)泛型把緩存對(duì)象存儲(chǔ)在軟引用里面(map里面)
代碼如下:
public class GoodsCache { private volatile static GoodsCache goodsCache; public GoodsCache(){ this.cache = new HashMap<>(); } public static GoodsCache getGoodsCache() { if(goodsCache == null) { synchronized (GoodsCache.class){ if(goodsCache == null){ goodsCache = new GoodsCache(); } } } return goodsCache; } // 將緩存對(duì)象存儲(chǔ)在軟引用中 private Map<String, SoftReference<Goods>> cache; // 根據(jù)id存儲(chǔ)緩存Goods對(duì)象 public void setCache(Goods goods) { cache.put(goods.getId(), new SoftReference<Goods>(goods)); System.out.println("添加數(shù)據(jù)到緩存成功"); } // 根據(jù)id從緩存中獲取對(duì)象 public Goods getCache(String id) { // 根據(jù)id,獲取緩存對(duì)象的軟引用 SoftReference<Goods> softRef = cache.get(id); return softRef == null ? null : softRef.get(); } public void delCache(String id) { cache.remove(id); System.out.println("從緩存刪除數(shù)據(jù)成功"); } }
goodsservice模擬數(shù)據(jù)庫(kù)增刪改查
接下來(lái)我們書(shū)寫(xiě)goodsservice代碼,來(lái)模擬數(shù)據(jù)庫(kù)增刪改查,不過(guò)我們是通過(guò)id來(lái)進(jìn)行
public class GoodsService { GoodsCache goodsCache = GoodsCache.getGoodsCache(); public Goods getById(String id){ if(goodsCache.getCache(id) == null){ Goods goods = GoodsBase.getBase().get(id); goodsCache.setCache(goods); System.out.println("從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)"); System.out.println(goods.getName()); return goods; } System.out.println(goodsCache.getCache(id).getName()); return goodsCache.getCache(id); } public void add(Goods goods){ goodsCache.setCache(goods); GoodsBase.getBase().put(goods.getId(), goods); System.out.println("添加數(shù)據(jù)到數(shù)據(jù)庫(kù)"); } public void deleteById(String id){ if(goodsCache.getCache(id) != null){ goodsCache.delCache(id); } GoodsBase.getBase().remove(id); } }
最后我們書(shū)寫(xiě)test文件
運(yùn)行結(jié)果
可以看到第二次運(yùn)行 goodsService.getById("1");
是從緩存中直接讀取的數(shù)據(jù),也可以看出,其實(shí)用軟引用實(shí)現(xiàn)緩存機(jī)制,讀取的對(duì)象是同一個(gè)對(duì)象。
以上就是java使用軟引用實(shí)現(xiàn)緩存機(jī)制示例的詳細(xì)內(nèi)容,更多關(guān)于java軟引用緩存機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java(TM) Platform SE binary 打開(kāi)jar文件的操作
這篇文章主要介紹了Java(TM) Platform SE binary 打開(kāi)jar文件的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Java 騰訊驗(yàn)證碼平臺(tái)使用實(shí)例
這篇文章主要介紹了Java 騰訊驗(yàn)證碼平臺(tái)使用實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02JAVA動(dòng)態(tài)維度笛卡爾積輸出的實(shí)現(xiàn)
本文主要介紹了JAVA動(dòng)態(tài)維度笛卡爾積輸出的實(shí)現(xiàn),通過(guò)動(dòng)態(tài)生成笛卡爾積,可以方便地處理多維數(shù)據(jù)集,提高數(shù)據(jù)處理效率,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Java通過(guò)正則表達(dá)式獲取域名簡(jiǎn)單示例
在Java中可以使用正則表達(dá)式來(lái)從字符串中匹配和提取域名,下面這篇文章主要給大家介紹了關(guān)于Java通過(guò)正則表達(dá)式獲取域名的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12在IDEA中搭建最小可用SpringMVC項(xiàng)目(純Java配置)
這篇文章主要介紹了在IDEA中搭建最小可用SpringMVC項(xiàng)目(純Java配置),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Spring中@PropertySource注解使用場(chǎng)景解析
這篇文章主要介紹了Spring中@PropertySource注解使用場(chǎng)景解析,@PropertySource注解就是Spring中提供的一個(gè)可以加載配置文件的注解,并且可以將配置文件中的內(nèi)容存放到Spring的環(huán)境變量中,需要的朋友可以參考下2023-11-11小程序與后端Java接口交互實(shí)現(xiàn)HelloWorld入門(mén)
本文主要介紹了小程序與后端Java接口交互實(shí)現(xiàn)HelloWorld入門(mén) ,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07