自研锁逻辑
按照JUC里面java.util.concurrent.locks.Lock接规范编写
lock加锁关键逻辑
- 加锁的Lua脚本,通过redis里面的hash数据模型,加锁和可重入性都要保证
- 加锁不成,需要while进行重试并自旋
- 自动续期,加个钟
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException
{
if(time == -1L)
{
//第一步
String script =
"if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then " +
"redis.call('hincrby',KEYS[1],ARGV[1],1) " +
"redis.call('expire',KEYS[1],ARGV[2]) " +
"return 1 " +
"else " +
"return 0 " +
"end";
System.out.println("lockName:"+lockName+"\t"+"uuidValue:"+uuidValue);
//第二步
while(!stringRedisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class), Arrays.asList(lockName), uuidValue,String.valueOf(expireTime)))
{
//暂停60毫秒
try {
TimeUnit.MILLISECONDS.sleep(60); } catch (InterruptedException e) {
e.printStackTrace(); }
}
//第三步:新建一个后台扫描程序,来坚持key目前的ttl,是否到我们规定的1/2 1/3来实现续期
renewExpire();
return true;
}
return false;
}
Redlock红锁算法
https://redis.io/docs/manual/patterns/distributed-locks/
自研锁存在的问题
线程 1 首先获取锁成功,将键值对写入 redis 的 master 节点,在 redis 将该键值对同步到 slave 节点之前,master 发生了故障;redis 触发故障转移,其中一个 slave 升级为新的 master,此时新上位的master并不包含线程1写入的键值对,因此线程 2 尝试获取锁也可以成功拿到锁,此时相当于有两个线程获取到了锁,可能会导致各种预期之外的情况发生,例如最常见的脏数据。
我们加的是排它独占锁,同一时间只能有一个建redis锁成功并持有锁,严禁出现2个以上的请求线程拿到锁。危险的
Redlock算法设计理念
Redis也提供了Redlock算法,用来实现基于多个实例的分布式锁。
锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作。
Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用。
Redison
@Autowired
private Redisson redisson;
public String saleByRedisson()
{
String retMessage = "";
RLock redissonLock = redisson.getLock("zzyyRedisLock");
redissonLock.lock();
try
{
//1 查询库存信息
String result = stringRedisTemplate.opsForValue().get("inventory001");
//2 判断库存是否足够
Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
//3 扣减库存,每次减少一个
if(inventoryNumber > 0)
{
stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
retMessage = "成功卖出一个商品,库存剩余:"+inventoryNumber;
System.out.println(retMessage+"\t"+"服务端口号"+port);
}else{
retMessage = "商品卖完了,o(╥﹏╥)o";
}
}finally {
//改进点,只能删除属于自己的key,不能删除别人的
if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread())