单台tomcat上利用锁解决redis缓存击穿
1 可重入锁 ReentrantLock类
在解决缓存击穿时,使用了锁,在springboot项目中,直接基于java.util.concurrent.locks(JUC)获取可重入锁——ReentrantLock类
2 单台tomcat上利用锁解决缓存击穿
技术点:
1. try-catch-finally的finally下释放锁,而不能放在try下,要避免代码出错时导致代码运行不到释放锁的那行,导致锁无法释放
2. 加锁后,就要让其他没有获取到锁的进程睡眠等待一段时间,过段时间再请求获取锁,这个过程叫自旋
3. 使用log对象输出信息而不使用system.out.print,是因为后者影响性能
4. 要给锁加过期时间,避免代码执行到一半服务器断电,导致代码只执行一半,最后锁没能得到释放
public class UserServiceImpl implements UserService {
//声明log日志对象 相比system.out.pringt 更节省系统资源
Log log = LogFactory.getLog(UserServiceImpl.class);
//获取重复锁
private ReentrantLock lock = new ReentrantLock();
//获取操作redis的类
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public UserInfo getUser(Integer id) {
UserInfo userInfo = null;
String key = RedisConstants.USER_INFO_KEY + id;
String s = stringRedisTemplate.opsForValue().get(key); //查询redis缓存
if(s == null){ //redis缓存中不存在
try {
Thread.sleep(5000);
if(lock.tryLock()){ //通过ReentrantLock对象上锁
try {
//查询数据库,将数据库查询的对象保存至缓存中
log.info("查询了数据库");
userInfo = new UserInfo(id, "admin", "admin"); // 查询出的对象
String userInfoStr = JSON.toJSONString(userInfo);
stringRedisTemplate.opsForValue().set(key,userInfoStr,30,TimeUnit.DAYS);
} finally {
lock.unlock(); //释放锁
}
}else{ //锁被占用,没能获取到锁的线程
Thread.sleep(1000); //睡眠等待1秒钟
getUser(id);//递归,继续尝试获取锁并查询redis缓存
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{ //缓存命中
userInfo = JSON.parseObject(s, UserInfo.class);//从redis中取出数据
log.info("命中了缓存");
}
// 查询数据库
return userInfo;
}
}