分布式锁之Redis
文章目录
1. 前言
在Java并发编程中,我们通常使用到synchronized
、Lock
这两个线程锁,Java中的锁,只能保证对同一个JVM中的线程有效。而在分布式集群环境,这个时候我们就需要使用到分布式锁。
实现分布式锁的方案
- 基于数据库实现分布式锁
- 基于缓存Redis实现分布式锁
- 基于Zookeeper的临时序列化节点实现分布式锁
2. Redis实现分布式锁
场景:在高并发的情况下,可能有大量请求来到数据库查询三级分类数据,而这种数据不会经常改变,可以引入缓存来存储第一次从数据库查询出来的数据,其他线程就可以去缓存中获取数据,来减少数据库的查询压力。
在集群的环境下,就可以使用分布式锁来控制去查询数据库的次数。
2.1 阶段一
private Map<String, List<Catelog2Vo>> getCatalogJsonDBWithRedisLock() {
// 去Redis中抢占位置
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "1111");
if (lock){
// 抢到锁了 执行业务
Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
// 删除锁
stringRedisTemplate.delete("lock");
return dataFromDb;
}else {
// 自旋获取锁
// 休眠100ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getCatalogJsonDBWithRedisLock();
}
}
得到锁以后,我们应该再去缓存中确定一次,如果没有才需要继续查询,从数据库查到数据以后,应该先把数据放入缓存中,再将数据返回。
private Map<String, List<Catelog2Vo>> getDataFromDb() {
// 得到锁以后,我们应该再去缓存中确定一次,如果没有才需要继续查询
String catalogJson = stringRedisTemplate.opsForValue().get("catalogJson");
if (!StringUtils.isEmpty(catalogJson)) {
// 反序列化 转换为指定对象
Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJson, new
TypeReference<Map<String, List<Catelog2Vo>>>() {
});
return result;
}
System.out.println("查询数据库了......");
// 查询所有分类数据在进行刷选
List<CategoryEntity> categoryEntityList = baseMapper.selectList(null);
// 查询一级分类
List<CategoryEntity> leave1Categorys = getParent_cid(categoryEntityList, 0L);
Map<String, List<Catelog2Vo>> listMap = leave1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), l1 -> {
List<CategoryEntity> categoryL2List = getParent_cid(categoryEntityList, l1.getCatId());
List<Catelog2Vo> catelog2Vos = null;
if (categoryL2List != null) {
catelog2Vos = categoryL2List.stream().map(l2 -> {
Catelog2Vo catelog2Vo = new Catelog2Vo(l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
List<CategoryEntity> categoryL3List = getParent_cid(categoryEntityList,
l2.getCatId());
if (categoryL3List != null) {
List<Catelog2Vo.Catelog3Vo