使用 Redis 当做分布式锁的核心就是 Redis 的 setnx 命令,setnx 命令 Redis 会保证只有一个线程设置成功。
注意点:
- 释放锁操作必须在 finally 里执行,保证锁的成功释放。
- key 必须设置过期时间,避免因系统异常、线程崩溃导致 key 一直存在,引发死锁。
- 设置过期时间必须使用 setnx(key,vaule,expiredTime) 的原子操作。
- 释放锁,必须保证只能是持有锁的线程来进行释放,因此可以为每个线程分配个唯一编号,然后设置为 key 的值,删除时进行判断。
- key 的过期时间到了后,如果业务代码还未执行完,那么需要考虑为 key 延长过期时间,即续命。
最简陋的使用方式如下:
java
public String deductStock() throws InterruptedException {
String lockKey = "product_001";
// String clientId = UUID.randomUUID().toString();
RLock redissonLock = redisson.getLock(lockKey);
try {
// jedis.setnx(key,value)
// Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "zhuge");
//stringRedisTemplate.expire(lockKey,30, TimeUnit.SECONDS);
/* Boolean result = stringRedisTemplate.opsForValue()
.setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);
if (!result) {
return "1001";
}*/
// 加锁,实现锁续命功能
redissonLock.lock();
// jedis.get("stock")
int stock = Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get("stock")));
if (stock > 0) {
int realStock = stock - 1;
// jedis.set(key,value)
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock + "");
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
redissonLock.unlock();
// 这里也是有可能有问题的 有可能线程 A 刚进入到 if 里,然后被挂起了,而线程 B 加锁成功了,
// 此时也会发生线程 A 删除了线程 B 的锁
/*if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey);
}*/
}
return "end";
}
同一个商品的并发 --> 可以对库存进行分段,使用分段锁来提高并发