缓存带锁更新方案(细颗粒锁)
强一致性,更新数据库之后主动淘汰缓存,读请求更新缓存
避免缓存雪前,更新缓存的过程需要进行同步控制,同一时间只
允许一个请求访问数据库,为了保证数据的一致性还要加上缓存
失效时间;
这里就是演示一下思路
package cacheupdate;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Description: <br/>
* 缓存带锁更新方案
* <p>
* <br/>
* @Author: Qz1997
* @create 2021/5/1 14:03
*/
public class UserServiceImpl {
public String getUserInfo(Integer userId) {
// 1. 从redis 中获取
// if 有 直接返回
// 没有
// 2 加锁
acquireLock("Prefix" + userId);
try {
// 这里在查一次缓存的原因 是 如果多个线程 同时等待锁 第一个获得到锁的查库
// 之后的线程 都直接查缓存
// 现象就是 如: 1000个线程 同时到了22行 只有一个线程 会拿到这个细颗粒锁
// 插叙数据库 放入缓存 之后的999 个线程 都会查询缓存 减少数据库压力
// 3. 从redis 中获取
// if 有 直接返回
// 没有
// 4. 查询数据库
// 5. 放入缓存
} catch (Exception e) {
// 异常处理
} finally {
releaseLocck("Prefix" + userId);
}
// 6. 返回数据
return null;
}
// 以下是工具类代码
/**
* 细颗粒的所对象结合
*/
private static final ConcurrentHashMap<String, ReentrantLock> LOCK_MAP = new ConcurrentHashMap<>();
/**
* 释放锁
*
* @param key 锁的key
*/
public static void releaseLocck(String key) {
ReentrantLock lock = (ReentrantLock) getLockForKey(key);
// 判断锁是否是当前线程获取
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
/**
* 加锁
*
* @param key 锁的key
*/
@SuppressWarnings("all")
public static void acquireLock(String key) {
Lock lock = getLockForKey(key);
lock.lock();
}
/**
* 获取锁
*
* @param key 锁的key
* @return 锁
*/
private static Lock getLockForKey(String key) {
// 创建一把锁
ReentrantLock lock = new ReentrantLock();
// 如果传入key对应的value已经存在,就返回存在的value,不进行替换。如果不存在,就添加key和value,返回null
ReentrantLock previous = LOCK_MAP.putIfAbsent(key, lock);
return previous == null ? lock : previous;
}
}