ReadWriteLock:快速实现一个完备的缓存

1.缓存数据的初始化-一次性加载

在程序初始化的过程中调用put()方法,将数据一次性加载进缓存中

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;

public class Cache<K, V> {
    final Map<K, V> map = new HashMap<>();
    final ReadWriteLock rwl = new ReentrantReadWriteLock();
    //读锁
    final Lock r = rwl.readLock();
    //写锁
    final Lock w = rwl.writeLock();

    //读缓存
    V get(K key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }

    //写缓存
    V put(K key, V value) {
        w.lock();
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }
}

2.缓存数据的初始化-按需加载

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;

public class Cache2<K, V> {
    final Map<K, V> map = new HashMap<>();
    final ReadWriteLock rwl = new ReentrantReadWriteLock();
    final Lock r = rwl.readLock();
    final Lock w = rwl.writeLock();

    V put(K key, V value) {
        w.lock();
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }

    V get(K key) {
        V v = null;
        r.lock();
        try {
            v = map.get(key);
        } finally {
            r.unlock();
        }

        //缓存中存在,返回
        if(v != null) {
            return v;
        }

        //缓存中不存在,查询数据库
        w.lock();
        try {
            //再次验证,其他线程可能已经查询过数据库
            v = map.get(key);
            if(v == null) {
                //查询数据库
                System.out.println("数据查询中...");
                v = 数据库中的值;
                map.put(key, v);
            }
            return v;
        } finally {
            w.unlock();
        }
    }
}

3.锁升级

ReadWriteLock 并不支持这种升级,但是锁的降级却是允许的。以下代码来源自 ReentrantReadWriteLock 的官方示例,略做了改动。


class CachedData {
  Object data;
  volatile boolean cacheValid;
  final ReadWriteLock rwl =
    new ReentrantReadWriteLock();
  // 读锁  
  final Lock r = rwl.readLock();
  //写锁
  final Lock w = rwl.writeLock();
  
  void processCachedData() {
    // 获取读锁
    r.lock();
    if (!cacheValid) {
      // 释放读锁,因为不允许读锁的升级
      r.unlock();
      // 获取写锁
      w.lock();
      try {
        // 再次检查状态  
        if (!cacheValid) {
          data = ...
          cacheValid = true;
        }
        // 释放写锁前,降级为读锁
        // 降级是可以的
        r.lock(); ①
      } finally {
        // 释放写锁
        w.unlock(); 
      }
    }
    // 此处仍然持有读锁
    try {use(data);} 
    finally {r.unlock();}
  }
}

猜你喜欢

转载自blog.csdn.net/m0_37732829/article/details/111926030