在单体项目中,如果要保证程序只有一个线程进入,使用synchronized锁住代码即可,不过在分布式的情况下,synchronized只能锁住本地对象,如果其他的请求进入,synchronized是没有用的,使用redis的分布式锁;
代码:
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisisLock() {
//1.站分布式锁 redis占坑 //NS 如果设置的值没有的话就创建 成功返回ok 在创建的时候必须创建和设置锁的过期时间一起原子操作,才能万无一失
String uuid = UUID.randomUUID().toString();
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
if (lock) {
//设置锁的过期时间,不过如果在这里没设置之前就断电了,就麻烦了
// stringRedisTemplate.expire("lock",300,TimeUnit.SECONDS);
//站所成功
Map<String, List<Catelog2Vo>> dataFromDb;
try {
dataFromDb = getDataFromDb();
}finally {
//如果我们在获取uuid值对比的中间时已经过期,那么这种情况下要 对比+删除 一起原子操作 使用lua脚本
// stringRedisTemplate.delete("lock"); //释放锁 如果几个锁一起占用,不小心删除其他的锁改怎么办,就要创建uuid值,删除前对比创建的uuid跟获取的是否相等,再删除
String script ="if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
stringRedisTemplate.execute(new DefaultRedisScript<Integer>(script,Integer.class),Arrays.asList("lock"),uuid);
}
return dataFromDb;
} else {
//加锁失败重试
return getCatalogJsonFromDbWithRedisisLock(); //自旋方式 自旋锁也是阻塞锁,必须释放后其他的才能获取锁
}
}
在操作分布式的操作简单来说:
原子性加锁 + 业务代码 + 原子性解锁 = 安全分布式代码
1.先试用redis的加锁定时方法
2.判断当前有没有占用到锁,如果没有占用就递归,一直调用自己,占到锁为止
3.业务代码处理完后要释放锁,使用redis原子性操作,一定要放到finally中释放
释放锁的原子操作使用的是官方提供的lua脚本,一步到位,源码的意思是Arrays.asList("lock")去redis获取对应锁的数据,跟后面的uuid进行再脚本中比对,如果相等就操作redis删除key,否则返回0
来源:oschina
链接:https://my.oschina.net/u/4580084/blog/4899609