这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
上一节学习记录了如歌用redis编辑全局唯一id生成器,这节学习记录如何用redis实现锁。
引言
锁的用处很多,目的是为了维护多线程数据安全,例如抢票系统,如何防止多线程下票出现超卖的问题,给票数据上锁是个很不错的选择。除了买票场景外,还有很多场景用的上锁,只要是为了防止数据脏改,基本都可以给数据上锁。锁的实现方式有很多,最好根据自己的业务场景和性能需求有所取舍。
锁
- 悲观锁
- 认为线程安全问题一定会发生,在执行数据操作前先获取锁,确保线程串行执行
- 线程串行执行,性能差
- 乐观锁
- 只有在更新数据时才去判断有没有其他线程对数据做了修改
- 方式
- 版本号法
- CAS法:直接比对修改的数据来判断目标是否被其它线程修改过,如查出的库存为10,修改时比对库存是否仍为10
- 成功率可能较低
代码编写
redis有个方法setNx,若不存在某个key,则缓存数据并放回成功信息,若key存在,则不缓存数据并返回失败信息,我们可以利用这一点编写锁的代码。
- 注入
@Autowired
private StringRedisTemplate stringRedisTemplate;
- setNx
/**
* 字符串数据缓存至redis,已存在则不缓存
* @param key
* @param value
* @return
*/
public boolean setNx(String key,String value){
return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, value));
}
/**
* 字符串数据缓存至redis并设置过期时间,已存在则不缓存
* @param key
* @param value
* @param timeout
* @param unit
* @return
*/
public boolean setNx(String key,String value,long timeout,TimeUnit unit){
return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit));
}
- 上锁和解锁
/**
* 上锁
* @param key
* @return
*/
public boolean tryLock(String key) {
return this.setNx(key, "lock", 10, TimeUnit.SECONDS);
}
public boolean tryLock(String key,String value,long ttl) {
return this.setNx(key,value,ttl,TimeUnit.SECONDS);
}
/**
* 解锁
* @param key
*/
public void unlock(String key) {
this.delete(key);
}