分布式锁应用场景大都是用在高并发,大流量场景。当多个进程不在同一个系统中,就需要用分布式锁控制多个进程对资源的访问。本篇介绍的是基于 Redis 实现的分布式锁。首先看两个 Redis 的命令:
SETNX key value
将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。
GETSET key value
自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。
以秒杀为例,基于 Redis 实现的分布式锁,其实就是在秒杀的方法前后进行加锁、解锁操作:
public void orderProductMockDiffUser(String productId) {
// 加锁
// ...
// 解锁
}
下面新建一个 Redis 分布式锁的处理,把加锁和解锁写进来:
@Component
@Slf4j
public class RedisLock {
@Resource
private StringRedisTemplate redisTemplate;
/**
* 加锁
* @param key
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key, String value) {
//SETNX命令, 可以设置返回true, 不可以返回false
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期
if (StringUtils.isEmpty(currentValue) && (Long.parseLong(currentValue) < System.currentTimeMillis())) {
//GETSET命令, 获取上一个锁的时间
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(value)) {
return true;
}
}
return false;
}
/**
* 解锁
*/
public void unLock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Throwable e) {
log.error("[redis分布式锁] 解锁异常, {}", e.getMessage(), e);
}
}
}
使用就很简单了
private static final int TIMEOUT = 10 * 1000; //超时时间10秒
@Autowired
private RedisLock redisLock;
/**
* 秒杀的方法
*/
public void orderProductMockDiffUser(String productId) {
//加锁
long time = System.currentTimeMillis() + TIMEOUT;
if(!redisLock.lock(productId, String.valueOf(time))) {
throw new SellException(101, "人也太多了, 换个姿势再试试~");
};
//...
//解锁
redisLock.unLock(productId, String.valueOf(time));
}
这种方式实现的分布式锁是有缺点的,详情请点击我的下一篇博客