网站大图怎么做更吸引客户,南京网站开发建设,百度下载安装到桌面上,班级网页设计模板html基于Redis实现分布式锁——Java版本 版本一版本二版本三Redisson 定义分布式锁接口如下#xff1a;
public interface ILock {boolean tryLock(long timeoutSec);void unlock();
}版本一
设定业务超时时间#xff0c;到期自动解锁。缺点是超时时间不好估计#xff0c;需要… 基于Redis实现分布式锁——Java版本 版本一版本二版本三Redisson 定义分布式锁接口如下
public interface ILock {boolean tryLock(long timeoutSec);void unlock();
}版本一
设定业务超时时间到期自动解锁。缺点是超时时间不好估计需要略大于业务执行的时间。当超时时间小于执行业务时间时其他线程会拿到锁而之前的线程执行完后又会解锁变得混乱导致线程安全问题。
public class SimpleRedisLock implements ILock{Autowiredprivate StringRedisTemplate stringRedisTemplate;private String name;private static final String KEY_PREFIX lock:;Overridepublic boolean tryLock(long timeoutSec) {Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, Thread.currentThread().getId() ,timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}Overridepublic void unlock() {stringRedisTemplate.delete(KEY_PREFIX name);}
}版本二
解锁时判断锁是否和自己假的锁标识一样标识使用UUID线程ID标识一样才释放锁。每个线程都会创建一个SimpleLock因此保证UUID不一样。
public class SimpleRedisLock implements ILock{private StringRedisTemplate stringRedisTemplate;private String name;private static final String KEY_PREFIX lock:;private static final String ID_PREFIX UUID.randomUUID().toString() -;public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate stringRedisTemplate;this.name name;}Overridepublic boolean tryLock(long timeoutSec) {String value ID_PREFIX Thread.currentThread().getId();Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, value,timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}Overridepublic void unlock() {String value ID_PREFIX Thread.currentThread().getId();String id stringRedisTemplate.opsForValue().get(KEY_PREFIX name);if (value.equals(id)) {stringRedisTemplate.delete(KEY_PREFIX name);}}
}版本三
某个线程先判断锁是自己的此时由于其他原因阻塞比如Full GC其他线程拿到锁之前的线程再解锁但是解的并不是自己的锁导致线程安全问题。 需要保证这些操作的原子性。使用Lua脚本。 使用redis提供的函数call。key类型参数放入KEYS数组其他参数放入ARGV数组Lua中数组角标从1开始。Lua脚本如下。
if (redis.call(get, KEYS[1]) ARGV[1]) thenreturn redis.call(del, KEYS[1])
end
return 0Lua脚本放在resources文件夹下在Java代码中调用StringRedisTemplate的execute方法执行Lua脚本。最后分布式锁代码为
public class SimpleRedisLock implements ILock{private StringRedisTemplate stringRedisTemplate;private String name;private static final String KEY_PREFIX lock:;private static final String ID_PREFIX UUID.randomUUID().toString() -;private static final DefaultRedisScriptLong UNLOCK_SCRIPT;static {UNLOCK_SCRIPT new DefaultRedisScript();UNLOCK_SCRIPT.setLocation(new ClassPathResource(unlock.lua));UNLOCK_SCRIPT.setResultType(Long.class);}public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate stringRedisTemplate;this.name name;}Overridepublic boolean tryLock(long timeoutSec) {String value ID_PREFIX Thread.currentThread().getId();Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, value,timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}Overridepublic void unlock() {stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX name),ID_PREFIX Thread.currentThread().getId());}
}
Redisson
上述实现的分布式锁缺点为 1、不可重入同一线程不能对同一把锁多次加锁。 2、不可重试。 3、超时时间不好设置有可能超时自动释放虽然不会有误删但是存在其他线程重新加锁。 4、主从复制的单点问题主节点宕机导致从节点锁还没有同步。 这些功能属于拓展功能要么出现概率低要么可以不需要这样的需求。 Redisson包含分布式锁的成熟实现。
1、Redisson的可重入锁实现原理 参考ReentrantLock原理需要存储加锁次数。因此使用Redis中的Hash数据结构。key是锁名称field是UUID线程idvalue是加锁次数。 2、Redisson的可重试锁和超时释放实现原理 while持续在重试时间内重试但不是一直重试而是消息订阅和信号量释放了再来重试。 超时释放使用了看门狗机制每10秒钟续期30秒无限续期直到调用unLock方法。 3、Redisson解决主从一致性问题的原理 去中心化不要主从每个节点都需要获取锁使用了红锁算法。N个节点需要获取N/21个锁才能加锁成功。使用Multilock。 缺点是增加读写。 对每个节点都使用配置类把Bean加载到容器中。
RLock lock1 redissonClient.getLock(order);
RLock lock2 redissonClient2.getLock(order);
RLock lock3 redissonClient3.getLock(order);
RLock lock redissonClient.getMultiLock(lock1, lock2, lock3);