一.SETNX

1.实现原理

利用redis中的set命令来实现分布式锁。

从Redis 2.6.12版本开始,set可以使用下列参数:

SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]

EX second :设置键的过期时间为second秒。 SET key value EX second效果等同于SETEX key second value 。
PX millisecond :设置键的过期时间为millisecond毫秒。 SET key value PX millisecond效果等同于PSETEX key millisecond value 。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX效果等同于SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。‘

2.SETNX

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁,比如setnx sku HBL-89H-OL1。

当且仅当 key 不存在,将 key 的值设为 value。 若给定的 key 已经存在,则 SETNX 不做任何动作

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

返回值:设置成功,返回 1 。设置失败,返回 0 。

3.代码实例

 @Autowired
    private RedisLock redisLock;
    
 @ApiOperation(value = "redisLock")
    @PostMapping("/pc/redisLock")
    public ZTResponseMessage stockmanage(@RequestParam String sku){
        ZTResponseMessage ztms = new ZTResponseMessage();
        Long starttimes = System.currentTimeMillis();
        try {
            //加锁
            long time = System.currentTimeMillis() + 1000 * 20;  //超时时间:20秒,最好设为常量
            boolean isLock = redisLock.lock(sku, String.valueOf(time));
            if (!isLock) {
                ztms.setCode(102);
                ztms.setDesc("SKU:" + sku + "还有其他线程在进行操作!");
                ztms.setSuccess(false);
                return ztms;
            }
            Thread.sleep(10000);
            redisLock.unlock(sku,String.valueOf(time));
            ztms.setCode(102);
            ztms.setDesc("SKU:" + sku + "操作成功!");
            ztms.setSuccess(false);
            return ztms;
        } catch (Exception e) {
            ztms.setCode(102);
            ztms.setDesc("Exception异常提示:" + e.getMessage());
            ztms.setSuccess(false);
        }
        Long endtimes = System.currentTimeMillis();
        ztms.setSecond(endtimes - starttimes);
        return ztms;
    }
RedisLock 代码实现
@Component
public class RedisLock {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加锁
     * @param key   商品id
     * @param value 当前时间+超时时间
     * @return
     */
    public boolean lock(String key, String value) {
        if (redisTemplate.opsForValue().setIfAbsent(key, value)) {     //这个其实就是setnx命令,只不过在java这边稍有变化,返回的是boolea
            return true;
        }

        //避免死锁,且只让一个线程拿到锁
        String currentValue = redisTemplate.opsForValue().get(key);
        //如果锁过期了
        if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间
            String oldValues = redisTemplate.opsForValue().getAndSet(key, value);

            /*
               只会让一个线程拿到锁
               如果旧的value和currentValue相等,只会有一个线程达成条件,因为第二个线程拿到的oldValue已经和currentValue不一样了
             */
            if (!StringUtils.isEmpty(oldValues) && oldValues.equals(currentValue)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 解锁
     * @param key
     * @param value
     */
    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 (Exception e) {
            logger.error("『redis分布式锁』解锁异常,{}", e);
        }
    }
}

二.Redisson实现分布式锁

    @Autowired
    private Redisson redisson;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @ApiOperation(value = "redisLock")
    @PostMapping("/pc/redisLock")
    public String redisLock(){
        String lockKey="lockKey";
        RLock redisId=redisson.getLock(lockKey);//拿到锁对象
       try {  //问题一
           redisId.lock();//j加锁
           int stock=Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock");
           if(stock>0){
               int realStock=stock-1;
               stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)
           }else{
               System.out.println("扣减失败,库存不足");
           }
       }finally {
               redisId.unlock();
   }
    return "end";
}