ReadWriteLock 实现内存缓存

255 阅读2分钟

ReadWriteLock

读多写少场景;缓存;锁升级;

读写锁遵循的规则

  1. 允许多个线程同时读共享变量;
  2. 只允许一个线程写共享变量;
  3. 如果一个写线程正在执行写操作,此时禁止读线程读共享变量。

具体实现类:ReentrantReadWriteLock

从这个实现类命名便可得知其支持可重入,其也实现了Lock接口,读锁之间共享资源,写锁之间互斥;支持锁降级不支持锁升级;读锁为释放不能申请写锁,写锁未释放可以申请读锁;

读写锁类似于 ReentrantLock,也支持公平模式和非公平模式。读锁和写锁都实现了java.util.concurrent.locks.Lock 接口,所以除了支持 lock() 方法外,tryLock()、lockInterruptibly() 等方法也都是支持的。但是有一点需要注意,那就是只有写锁支持条件变量,读锁是不支持条件变量的,读锁调用 newCondition() 会抛出UnsupportedOperationException 异常。

锁降级的意义?

提高一边写一边读的效率,部分更新完成后就降级为读锁;防止写锁的一直占用阻塞大量读锁;

官方示例:

public void test(){
        boolean cacheValid = false;
        r.lock();
        if (!cacheValid){
            // 释放读锁,因为不允许读锁的升级
            r.unlock();
            // 获取写锁
            w.lock();
            try {
                //写缓存操作
                cacheValid = true;
                // 释放写锁前降级为读锁
                r.lock();
            }finally {
                w.unlock();
            }
        }
        try {
            // 此处仍然持有读锁 对读取的数据进行操作
        }finally {
            r.unlock();
        }

    }

利用特性实现缓存:

/**
 * @author zhan
 * @version 2021/7/1
 */
public class CustomerCache<K,V> {

    private final ReentrantReadWriteLock rlk = new ReentrantReadWriteLock();
    private final Lock r = rlk.readLock();
    private final Lock w = rlk.writeLock();
    private final HashMap<K, V> map = new HashMap<>();

    public CustomerCache(){
    }

    public V get(K k){
        r.lock();
        try {
            return map.get(k);
        }finally {
            r.unlock();
        }
    }

    public void set(K k, V v){
        w.lock();
        try {
            map.put(k, v);
        }finally {
            w.unlock();
        }
    }

    public V getIfNullPut(K k){
        V v;
        r.lock();
        try {
            v = map.get(k);
        }finally {
            r.unlock();
        }
        if (v != null){
            return v;
        }
        w.lock();
        try {
            // 二次校验,可能被别的线程添加了缓存
            v = map.get(k);
            if (v == null){
                v =  selectDb();
                return map.put(k, v);
            }
        }finally {
            w.unlock();
        }
        return v;
    }

    public V selectDb(){
        return null;
    }
}