Java併發編程入門(十三)讀寫鎖和緩存模板

Java極客  |  做者  /  鏗然一葉
這是Java極客的第 41 篇原創文章

1、讀寫鎖

提到讀寫鎖,都能想到是鎖優化方式之一的鎖分離,實現效果是讀讀不互斥,讀寫互斥,寫寫互斥。java

讀寫鎖自己比較簡單,下面經過一個例子看看讀寫鎖的使用。spring

1.Cache是一個抽象類,實現了緩存的基本方法,子類只須要實現init方法初始化緩存數據, 讀寫鎖在此類中應用。

2.CacheManager是緩存管理類,緩存相關的操做均以它做爲入口,集中管理。數據庫

3.CacheKey是個常量定義類,定義了每一個緩存的key,例如證件類型緩存key,行業緩存key等等,訪問指定緩存時經過定義的常量key來訪問。編程

2、Show me code

I、Cache.java

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/** * @ClassName Cache * @Description 緩存抽象類 * @Author 鏗然一葉 * @Date 2019/10/6 10:59 * @Version 1.0 * javashizhan.com **/
public abstract class Cache<K,V> {

    final Map<K, V> m = new HashMap<K,V>();

    final ReadWriteLock rwl = new ReentrantReadWriteLock();
    final Lock r = rwl.readLock();
    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();
        }
    }

    public V remove(K key) {
        w.lock();
        try {
            return m.remove(key);
        } finally {
            w.unlock();
        }
    }

    /** * 模板方法,調用子類實現的init方法 */
    void _init() {
        w.lock();
        try {
            init();
        } finally {
            w.unlock();
        }
    }

    /** * 緩存初始化,子類實現 */
    protected abstract void init();
}
複製代碼

II、CacheKey.java

public class CacheKey {
    public static final Integer CERTIFICATE_TYPE = 1001;
    public static final Integer INDUSTRY = 1002;
}
複製代碼

III、CacheManager.java

import java.util.Hashtable;
import java.util.Map;

/** * @ClassName CacheManager * @Description 緩存管理 * @Author 鏗然一葉 * @Date 2019/10/6 11:11 * @Version 1.0 * javashizhan.com **/
public class CacheManager<K, V> {

    private  Map<Integer, Cache<K, V>> cacheMap = new Hashtable<Integer, Cache<K, V>>();

    /** 私有構造器 */
    private CacheManager() {}

    private static CacheManager cacheManager;

    /** * 單例模式 * @return */
    public static CacheManager getInstance() {
        if (null == cacheManager) {
            synchronized (CacheManager.class) {
                if (null == cacheManager) {
                    cacheManager = new CacheManager();
                }
            }
        }
        return cacheManager;
    }

    /** * 註冊緩存 * @param cacheKey 緩存key * @param cache */
    public void registerCache(Integer cacheKey, Cache<K, V> cache) {
        cache._init();
        cacheMap.put(cacheKey, cache);
    }

    /** * 從指定緩存中獲取數據 * @param cacheKey 緩存Key * @param key * @return */
    public V getValue(Integer cacheKey, K key) {
        Cache<K, V> cache = cacheMap.get(cacheKey);
        return cache.get(key);
    }

    /** * 設置緩存 * @param cacheKey 緩存Key * @param key * @param value */
    public void put(Integer cacheKey, K key, V value) {
        Cache<K, V> cache = cacheMap.get(cacheKey);
        cache.put(key, value);
    }

    /** * 從指定緩存中刪除數據 * @param cacheKey 緩存Key * @param key */
    public V remove(Integer cacheKey, K key) {
        Cache<K, V> cache = cacheMap.get(cacheKey);
        return cache.remove(key);
    }
}
複製代碼

IV、IndustryCache.java

/** * @ClassName IndustryCache * @Description 行業緩存 * @Author 鏗然一葉 * @Date 2019/10/6 11:38 * @Version 1.0 * javashizhan.com **/
public class IndustryCache extends Cache {

    /** * 初始化緩存,能夠自行實現,例如從數據庫中讀取數據 */
    @Override
    public void init() {
        put("01", "建築建材");
        put("02", "冶金礦產");
        put("03", "石油化工");
        put("04", "水利水電");
    }
}
複製代碼

V、CertificateTypeCache.java

/** * @ClassName CertificateTypeCache * @Description 證件類型緩存 * @Author 鏗然一葉 * @Date 2019/10/6 11:32 * @Version 1.0 * javashizhan.com **/
public class CertificateTypeCache extends Cache {

    /** * 初始化緩存,能夠自行實現,例如從數據庫中讀取數據 */
    @Override
    public void init() {
        put("01","身份證");
        put("02","護照");
        put("03","軍官證");
        put("04","學生證");
    }
}
複製代碼

VI、使用例子

public class CacheTest {

    public static void main(String[] args) {

        //初始化緩存
        Cache certificateTypeCache = new CertificateTypeCache();
        Cache industryCache = new IndustryCache();
        CacheManager.getInstance().registerCache(CacheKey.CERTIFICATE_TYPE, certificateTypeCache);
        CacheManager.getInstance().registerCache(CacheKey.CERTIFICATE_TYPE, industryCache);

        //獲取證件類型
        Object value = CacheManager.getInstance().getValue(CacheKey.CERTIFICATE_TYPE,"01");
        System.out.println("value: " + value);

        //證件類型緩存添加數據
        CacheManager.getInstance().put(CacheKey.CERTIFICATE_TYPE, "99", "警官證");
        value = CacheManager.getInstance().getValue(CacheKey.CERTIFICATE_TYPE,"99");
        System.out.println("value: " + value);

        //證件類型緩存移除數據
        CacheManager.getInstance().remove(CacheKey.CERTIFICATE_TYPE, "99");
        value = CacheManager.getInstance().getValue(CacheKey.CERTIFICATE_TYPE,"99");
        System.out.println("value: " + value);

    }
}
複製代碼

輸出日誌:緩存

value: 建築建材
value: 警官證
value: null
複製代碼

3、緩存初始化和註冊

1.緩存初始化方式能夠是懶加載或者勤快加載,能夠根據須要實現。安全

2.Spring中勤快加載能夠在spring初始化過程當中完成,例如啓動類中,另一種比較好的方式是每一個緩存類自行完成加載和註冊,代碼參考以下:bash

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

/** * @ClassName IndustryCache * @Description TODO * @Author 鏗然一葉 * @Date 2019/10/6 13:08 * @Version 1.0 * javashizhan.com **/
@Component
public class IndustryCache extends Cache {

    /** * 能夠自行實現,例如從數據庫中讀取數據 */
    @Override
    public void init() {
        put("01", "建築建材");
        put("02", "冶金礦產");
        put("03", "石油化工");
        put("04", "水利水電");


    }

    @PostConstruct
    public void register() {
        CacheManager.getInstance().registerCache(CacheKey.INDUSTRY, this);
    }
}
複製代碼

1.首先是在每一個緩存類上添加@Component註解,使得Spring啓動時會加載這個類到內存中。併發

2.其次在每一個緩存類中實現一個緩存註冊方法,並添加@PostConstruct註解,使得緩存類被spring實例化後會自動調用該方法。ide

這樣每一個緩存的初始化和註冊就只和本身有關,實現了職責分離,內聚以及解耦。post

end.


相關閱讀:
Java併發編程(一)知識地圖
Java併發編程(二)原子性
Java併發編程(三)可見性
Java併發編程(四)有序性
Java併發編程(五)建立線程方式概覽
Java併發編程入門(六)synchronized用法
Java併發編程入門(七)輕鬆理解wait和notify以及使用場景
Java併發編程入門(八)線程生命週期
Java併發編程入門(九)死鎖和死鎖定位
Java併發編程入門(十)鎖優化
Java併發編程入門(十一)限流場景和Spring限流器實現
Java併發編程入門(十二)生產者和消費者模式-代碼模板
Java併發編程入門(十四)CountDownLatch應用場景
Java併發編程入門(十五)CyclicBarrier應用場景
Java併發編程入門(十六)秒懂線程池差異
Java併發編程入門(十七)一圖掌握線程經常使用類和接口
Java併發編程入門(十八)再論線程安全


Java極客站點: javageektour.com/

相關文章
相關標籤/搜索