Redis学习笔记(六) | 青训营笔记

89 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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);
}