Java并发编程入门(十三)读写锁和缓存模板

2,316 阅读5分钟

banner窄.png

铿然架构  |  作者  /  铿然一叶 这是铿然架构的第 43 篇原创文章

相关阅读:

Java并发编程(一)知识地图
Java并发编程(二)原子性
Java并发编程(三)可见性
Java并发编程(四)有序性
Java并发编程(五)创建线程方式概览
Java并发编程入门(六)synchronized用法
Java并发编程入门(七)轻松理解wait和notify以及使用场景
Java并发编程入门(八)线程生命周期
Java并发编程入门(九)死锁和死锁定位
Java并发编程入门(十)锁优化
Java并发编程入门(十一)限流场景和Spring限流器实现
Java并发编程入门(十二)生产者和消费者模式-代码模板
Java并发编程入门(十四)CountDownLatch应用场景
Java并发编程入门(十五)CyclicBarrier应用场景
Java并发编程入门(十六)秒懂线程池差别
Java并发编程入门(十七)一图掌握线程常用类和接口
Java并发编程入门(十八)再论线程安全
Java并发编程入门(十九)异步任务调度工具CompleteFeature
Java并发编程入门(二十)常见加锁场景和加锁工具


一、读写锁

提到读写锁,都能想到是锁优化方式之一的锁分离,实现效果是读读不互斥,读写互斥,写写互斥。

读写锁本身比较简单,下面通过一个例子看看读写锁的使用。

1.Cache是一个抽象类,实现了缓存的基本方法,子类只需要实现init方法初始化缓存数据, 读写锁在此类中应用。

2.CacheManager是缓存管理类,缓存相关的操作均以它作为入口,集中管理。

3.CacheKey是个常量定义类,定义了每个缓存的key,例如证件类型缓存key,行业缓存key等等,访问指定缓存时通过定义的常量key来访问。

二、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 volatile 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

三、缓存初始化和注册

1.缓存初始化方式可以是懒加载或者勤快加载,可以根据需要实现。

2.Spring中勤快加载可以在spring初始化过程中完成,例如启动类中,另外一种比较好的方式是每个缓存类自行完成加载和注册,代码参考如下:

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实例化后会自动调用该方法。

这样每个缓存的初始化和注册就只和自己有关,实现了职责分离,内聚以及解耦。

end.


<--阅过留痕,左边点赞 !