日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

十分鐘手擼一款線程安全的高性能通用緩存組件!

來源: 責編: 時間:2024-06-11 08:43:40 255觀看
導讀在實際工作中,有一種非常普遍的并發場景:那就是讀多寫少的場景。在這種場景下,為了優化程序的性能,我們經常使用緩存來提高應用的訪問性能。因為緩存非常適合使用在讀多寫少的場景中。在并發場景中,Java SDK中提供了ReadWr

pcG28資訊網——每日最新資訊28at.com

在實際工作中,有一種非常普遍的并發場景:那就是讀多寫少的場景。在這種場景下,為了優化程序的性能,我們經常使用緩存來提高應用的訪問性能。因為緩存非常適合使用在讀多寫少的場景中。pcG28資訊網——每日最新資訊28at.com

在并發場景中,Java SDK中提供了ReadWriteLock來滿足讀多寫少的場景。本文我們就來說說使用ReadWriteLock如何實現一個通用的緩存中心。pcG28資訊網——每日最新資訊28at.com

本文涉及的知識點有:pcG28資訊網——每日最新資訊28at.com

pcG28資訊網——每日最新資訊28at.com

讀寫鎖

說起讀寫鎖,相信小伙伴們并不陌生。總體來說,讀寫鎖需要遵循以下原則:pcG28資訊網——每日最新資訊28at.com

  • 一個共享變量允許同時被多個讀線程讀取到。
  • 一個共享變量在同一時刻只能被一個寫線程進行寫操作。
  • 一個共享變量在被寫線程執行寫操作時,此時這個共享變量不能被讀線程執行讀操作。

這里,需要小伙伴們注意的是:讀寫鎖和互斥鎖的一個重要的區別就是:讀寫鎖允許多個線程同時讀共享變量,而互斥鎖不允許。所以,在高并發場景下,讀寫鎖的性能要高于互斥鎖。但是,讀寫鎖的寫操作是互斥的,也就是說,使用讀寫鎖時,一個共享變量在被寫線程執行寫操作時,此時這個共享變量不能被讀線程執行讀操作。pcG28資訊網——每日最新資訊28at.com

讀寫鎖支持公平模式和非公平模式,具體是在ReentrantReadWriteLock的構造方法中傳遞一個boolean類型的變量來控制。pcG28資訊網——每日最新資訊28at.com

public ReentrantReadWriteLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();    readerLock = new ReadLock(this);    writerLock = new WriteLock(this);}

另外,需要注意的一點是:在讀寫鎖中,讀鎖調用newCondition()會拋出UnsupportedOperationException異常,也就是說:讀鎖不支持條件變量。pcG28資訊網——每日最新資訊28at.com

緩存實現

這里,我們使用ReadWriteLock快速實現一個緩存的通用工具類,總體代碼如下所示。pcG28資訊網——每日最新資訊28at.com

public class ReadWriteLockCache<K,V> {    private final Map<K, V> m = new HashMap<>();    private final ReadWriteLock rwl = new ReentrantReadWriteLock();    // 讀鎖    private final Lock r = rwl.readLock();    // 寫鎖    private final Lock w = rwl.writeLock();    // 讀緩存    public V get(K key) {        r.lock();        try { return m.get(key); }        finally { r.unlock(); }    }    // 寫緩存    public V put(K key, V value) {        w.lock();        try { return m.put(key, value); }        finally { w.unlock(); }    }}

可以看到,在ReadWriteLockCache中,我們定義了兩個泛型類型,K代表緩存的Key,V代表緩存的value。在ReadWriteLockCache類的內部,我們使用Map來緩存相應的數據,小伙伴都都知道HashMap并不是線程安全的類。pcG28資訊網——每日最新資訊28at.com

所以,這里使用了讀寫鎖來保證線程的安全性,例如,我們在get()方法中使用了讀鎖,get()方法可以被多個線程同時執行讀操作;put()方法內部使用寫鎖,也就是說,put()方法在同一時刻只能有一個線程對緩存進行寫操作。pcG28資訊網——每日最新資訊28at.com

這里需要注意的是:無論是讀鎖還是寫鎖,鎖的釋放操作都需要放到finally{}代碼塊中。pcG28資訊網——每日最新資訊28at.com

在以往的經驗中,有兩種向緩存中加載數據的方式,一種是:項目啟動時,將數據全量加載到緩存中,一種是在項目運行期間,按需加載所需要的緩存數據。pcG28資訊網——每日最新資訊28at.com

pcG28資訊網——每日最新資訊28at.com

接下來,我們就分別來看看全量加載緩存和按需加載緩存的方式。pcG28資訊網——每日最新資訊28at.com

全量加載緩存

全量加載緩存相對來說比較簡單,就是在項目啟動的時候,將數據一次性加載到緩存中,這種情況適用于緩存數據量不大,數據變動不頻繁的場景,例如:可以緩存一些系統中的數據字典等信息。整個緩存加載的大體流程如下所示。pcG28資訊網——每日最新資訊28at.com

pcG28資訊網——每日最新資訊28at.com

將數據全量加載到緩存后,后續就可以直接從緩存中讀取相應的數據了。pcG28資訊網——每日最新資訊28at.com

全量加載緩存的代碼實現比較簡單,這里,我就直接使用如下代碼進行演示。pcG28資訊網——每日最新資訊28at.com

public class ReadWriteLockCache<K,V> {    private final Map<K, V> m = new HashMap<>();    private final ReadWriteLock rwl = new ReentrantReadWriteLock();    // 讀鎖    private final Lock r = rwl.readLock();    // 寫鎖    private final Lock w = rwl.writeLock();        public ReadWriteLockCache(){        //查詢數據庫        List<Field<K, V>> list = .....;        if(!CollectionUtils.isEmpty(list)){            list.parallelStream().forEach((f) ->{    m.put(f.getK(), f.getV);   });        }    }    // 讀緩存    public V get(K key) {        r.lock();        try { return m.get(key); }        finally { r.unlock(); }    }    // 寫緩存    public V put(K key, V value) {        w.lock();        try { return m.put(key, value); }        finally { w.unlock(); }    }}

按需加載緩存

按需加載緩存也可以叫作懶加載,就是說:需要加載的時候才會將數據加載到緩存。具體來說:就是程序啟動的時候,不會將數據加載到緩存,當運行時,需要查詢某些數據,首先檢測緩存中是否存在需要的數據,如果存在,則直接讀取緩存中的數據,如果不存在,則到數據庫中查詢數據,并將數據寫入緩存。后續的讀取操作,因為緩存中已經存在了相應的數據,直接返回緩存的數據即可。pcG28資訊網——每日最新資訊28at.com

pcG28資訊網——每日最新資訊28at.com

這種查詢緩存的方式適用于大多數緩存數據的場景。pcG28資訊網——每日最新資訊28at.com

我們可以使用如下代碼來表示按需查詢緩存的業務。pcG28資訊網——每日最新資訊28at.com

class ReadWriteLockCache<K,V> {    private final Map<K, V> m = new HashMap<>();    private final ReadWriteLock rwl =  new ReentrantReadWriteLock();    private final Lock r = rwl.readLock();    private final Lock w = rwl.writeLock();    V get(K key) {        V v = null;        //讀緩存        r.lock();                try {            v = m.get(key);        } finally{            r.unlock();            }        //緩存中存在,返回        if(v != null) {              return v;        }          //緩存中不存在,查詢數據庫        w.lock();             try {     //再次驗證緩存中是否存在數據            v = m.get(key);            if(v == null){                 //查詢數據庫                v=從數據庫中查詢出來的數據                m.put(key, v);            }        } finally{            w.unlock();        }        return v;     }}

這里,在get()方法中,首先從緩存中讀取數據,此時,我們對查詢緩存的操作添加了讀鎖,查詢返回后,進行解鎖操作。判斷緩存中返回的數據是否為空,不為空,則直接返回數據;如果為空,則獲取寫鎖,之后再次從緩存中讀取數據,如果緩存中不存在數據,則查詢數據庫,將結果數據寫入緩存,釋放寫鎖。最終返回結果數據。pcG28資訊網——每日最新資訊28at.com

這里,有小伙伴可能會問:為啥程序都已經添加寫鎖了,在寫鎖內部為啥還要查詢一次緩存呢?pcG28資訊網——每日最新資訊28at.com

這是因為在高并發的場景下,可能會存在多個線程來競爭寫鎖的現象。例如:第一次執行get()方法時,緩存中的數據為空。如果此時有三個線程同時調用get()方法,同時運行到 w.lock()代碼處,由于寫鎖的排他性。此時只有一個線程會獲取到寫鎖,其他兩個線程則阻塞在w.lock()處。獲取到寫鎖的線程繼續往下執行查詢數據庫,將數據寫入緩存,之后釋放寫鎖。pcG28資訊網——每日最新資訊28at.com

此時,另外兩個線程競爭寫鎖,某個線程會獲取到鎖,繼續往下執行,如果在w.lock()后沒有 v = m.get(key); 再次查詢緩存的數據,則這個線程會直接查詢數據庫,將數據寫入緩存后釋放寫鎖。最后一個線程同樣會按照這個流程執行。pcG28資訊網——每日最新資訊28at.com

這里,實際上第一個線程已經查詢過數據庫,并且將數據寫入緩存了,其他兩個線程就沒必要再次查詢數據庫了,直接從緩存中查詢出相應的數據即可。pcG28資訊網——每日最新資訊28at.com

所以,在w.lock()后添加 v = m.get(key); 再次查詢緩存的數據,能夠有效的減少高并發場景下重復查詢數據庫的問題,提升系統的性能。pcG28資訊網——每日最新資訊28at.com

讀寫鎖的升降級

關于鎖的升降級,小伙伴們需要注意的是:在ReadWriteLock中,鎖是不支持升級的,因為讀鎖還未釋放時,此時獲取寫鎖,就會導致寫鎖永久等待,相應的線程也會被阻塞而無法喚醒。pcG28資訊網——每日最新資訊28at.com

雖然不支持鎖升級,但是ReadWriteLock支持鎖降級,例如,我們來看看官方的ReentrantReadWriteLock示例,如下所示。pcG28資訊網——每日最新資訊28at.com

class CachedData {    Object data;    volatile boolean cacheValid;    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();    void processCachedData() {        rwl.readLock().lock();        if (!cacheValid) {            // Must release read lock before acquiring write lock            rwl.readLock().unlock();            rwl.writeLock().lock();            try {                // Recheck state because another thread might have                // acquired write lock and changed state before we did.                if (!cacheValid) {                    data = ...                    cacheValid = true;                }                // Downgrade by acquiring read lock before releasing write lock                rwl.readLock().lock();            } finally {                rwl.writeLock().unlock(); // Unlock write, still hold read            }        }        try {            use(data);        } finally {            rwl.readLock().unlock();        }    }}}

數據同步問題

首先,這里說的數據同步指的是數據源和數據緩存之間的數據同步,說的再直接一點,就是數據庫和緩存之間的數據同步。pcG28資訊網——每日最新資訊28at.com

這里,我們可以采取三種方案來解決數據同步的問題,如下圖所示:pcG28資訊網——每日最新資訊28at.com

pcG28資訊網——每日最新資訊28at.com

超時機制

這個比較好理解,就是在向緩存寫入數據的時候,給一個超時時間,當緩存超時后,緩存的數據會自動從緩存中移除,此時程序再次訪問緩存時,由于緩存中不存在相應的數據,查詢數據庫得到數據后,再將數據寫入緩存。pcG28資訊網——每日最新資訊28at.com

定時更新緩存

這種方案是超時機制的增強版,在向緩存中寫入數據的時候,同樣給一個超時時間。與超時機制不同的是,在程序后臺單獨啟動一個線程,定時查詢數據庫中的數據,然后將數據寫入緩存中,這樣能夠在一定程度上避免緩存的穿透問題。pcG28資訊網——每日最新資訊28at.com

實時更新緩存

這種方案能夠做到數據庫中的數據與緩存的數據是實時同步的,可以使用阿里開源的Canal框架實現MySQL數據庫與緩存數據的實時同步。也可以使用我個人開源的mykit-data框架哦(推薦使用)~~pcG28資訊網——每日最新資訊28at.com

mykit-data開源地址:pcG28資訊網——每日最新資訊28at.com

  • https://github.com/sunshinelyz/mykit-data。
  • https://gitee.com/binghe001/mykit-data。

本文鏈接:http://m.www897cc.com/showinfo-26-92928-0.html十分鐘手擼一款線程安全的高性能通用緩存組件!

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Oh-My-Posh: 可定制且低延遲的跨平臺/跨Shell提示符渲染器

下一篇: 前端輪播圖,搞定!

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
狠狠色2019综合网| 国产精品社区| 亚洲欧美日韩另类| 欧美在线亚洲一区| 麻豆精品视频在线| 欧美日韩免费观看中文| 国产日产欧美一区| 亚洲欧洲精品一区二区| 一区二区三区高清| 久久精品国语| 欧美女同视频| 国产亚洲精品v| 亚洲精品资源美女情侣酒店| 午夜精品久久久久久久白皮肤 | 欧美在线影院在线视频| 猛干欧美女孩| 国产精品女人网站| 亚洲动漫精品| 午夜久久资源| 欧美精品久久久久a| 国产日韩精品综合网站| 亚洲另类自拍| 久久久久久网址| 欧美视频在线播放| 亚洲福利小视频| 性欧美videos另类喷潮| 欧美日韩国产成人精品| 国产欧美一区二区三区国产幕精品| 亚洲国产综合视频在线观看| 亚洲欧美日韩另类| 欧美精品1区2区3区| 国内揄拍国内精品久久| 亚洲亚洲精品三区日韩精品在线视频| 久热精品视频在线观看一区| 国产精品高潮呻吟久久av无限| 亚洲国产精品成人综合色在线婷婷| 亚洲一区二区在线免费观看| 欧美激情精品久久久久久大尺度 | 国产精品视频网| 亚洲精品五月天| 久久综合狠狠综合久久激情| 国产日本亚洲高清| 国产精品99久久久久久久女警| 欧美成人国产| 激情久久久久久久| 欧美一区二区成人6969| 欧美性大战久久久久| 最新亚洲一区| 噜噜噜久久亚洲精品国产品小说| 欧美不卡视频| 国产女主播一区二区| 一区二区三区免费观看| 欧美成人精品不卡视频在线观看| 国产麻豆成人精品| 亚洲一区二区三区在线视频| 欧美日本亚洲| 亚洲精品一区二区三区在线观看| 久久一区免费| 好看的日韩视频| 欧美在线播放一区二区| 国产精品一级| 亚洲女女女同性video| 欧美三级午夜理伦三级中文幕| 亚洲欧洲美洲综合色网| 久热精品视频在线观看| 精品96久久久久久中文字幕无| 欧美在线一级视频| 国产一区91精品张津瑜| 欧美在线你懂的| 国产无一区二区| 久久成人一区| 国产一区二区三区四区| 久久成人精品视频| 国产视频精品xxxx| 欧美影院成年免费版| 国产情侣一区| 久久疯狂做爰流白浆xx| 国产一区二区精品久久99| 久久国产天堂福利天堂| 国产一区 二区 三区一级| 久久精品五月| 尤物在线精品| 欧美电影免费观看大全| 亚洲肉体裸体xxxx137| 欧美精品一区在线发布| 一本色道精品久久一区二区三区| 欧美日韩中字| 亚洲一区二区三区中文字幕在线 | 久久精品国产第一区二区三区最新章节| 国产欧美日韩在线| 久久国产综合精品| 狠狠色伊人亚洲综合成人| 久久综合婷婷| 亚洲国产人成综合网站| 欧美精品久久久久久久久老牛影院| 日韩一区二区电影网| 国产精品xxxav免费视频| 亚洲欧美国产毛片在线| 国产亚洲午夜| 裸体歌舞表演一区二区| 亚洲精品小视频| 国产女精品视频网站免费| 亚洲国产精品va在线看黑人动漫| 欧美成人精品不卡视频在线观看| 日韩一二在线观看| 国产精品九九| 久久成人精品一区二区三区| 亚洲福利视频免费观看| 欧美日韩免费一区二区三区视频| 亚洲一区精品视频| 国产亚洲精品一区二区| 噜噜爱69成人精品| 夜夜嗨av一区二区三区网页 | 99精品欧美一区二区三区| 国产精品乱子乱xxxx| 久久久久国产一区二区| 亚洲日本成人| 国产精品性做久久久久久| 久久婷婷国产综合尤物精品| 亚洲精品无人区| 国产精品一区二区你懂得| 久久蜜臀精品av| 99精品视频免费观看| 国产日韩在线亚洲字幕中文| 免费欧美日韩国产三级电影| 中日韩视频在线观看| 国产视频在线观看一区二区三区 | 亚洲欧美日韩在线一区| 精品999在线观看| 欧美日韩免费在线| 欧美一区二区三区电影在线观看| 亚洲福利视频网站| 国产精品爱久久久久久久| 久久看片网站| 亚洲午夜精品| 亚洲第一天堂av| 国产欧美二区| 欧美激情综合网| 久久成人在线| 一本色道久久综合亚洲91| 狠狠色综合色区| 国产精品美女午夜av| 欧美国产日韩一区二区| 欧美在线观看日本一区| 99国产精品99久久久久久粉嫩| 国内精品美女在线观看| 欧美日韩三区四区| 蜜桃伊人久久| 欧美一区二区三区免费视| 99re热这里只有精品视频| 狠狠色狠色综合曰曰| 国产精品夜夜嗨| 欧美日韩精品一区| 免费不卡在线视频| 欧美中文字幕在线观看| 亚洲图片你懂的| 亚洲精品一区二区网址| 精品动漫3d一区二区三区免费| 国产精品日韩欧美一区二区三区| 欧美久久精品午夜青青大伊人| 久久精视频免费在线久久完整在线看| 亚洲午夜羞羞片| 亚洲免费观看在线视频| 在线观看国产精品淫| 国产日本欧洲亚洲| 国产精品毛片大码女人| 欧美激情视频一区二区三区在线播放 | 国产亚洲精品成人av久久ww| 欧美色精品在线视频| 欧美成人伊人久久综合网| 久久久久久久综合色一本| 午夜日韩视频| 亚洲一区二区三区高清| 国产精品永久免费| 欧美精品免费在线观看| 浪潮色综合久久天堂| 久久精品国产精品| 欧美一区二区视频观看视频| 亚洲欧美三级伦理| 亚洲一级免费视频| 一区二区三区毛片| 日韩视频在线观看一区二区| 亚洲区中文字幕| 亚洲国产综合在线看不卡| 永久久久久久| 狠色狠色综合久久| 黄色亚洲大片免费在线观看| 国产亚洲欧美一区| 国产日韩欧美黄色| 国产精品主播| 国产精品一二一区| 国产欧美二区| 国产亚洲成精品久久| 国产亚洲观看| 国产主播在线一区| 国外成人性视频| 一区二区视频免费在线观看| 国内精品久久国产| 黑丝一区二区三区| 伊人春色精品| 亚洲国产精品久久久| 亚洲国产精品成人综合色在线婷婷| 亚洲国产精品精华液2区45 | 久久精品国产99|