【2020.07.11】Springboot redis 分布式锁,分别使用 setnx 和 redisson 实现

1、线程不安全,使用synchronized 单体程序可以保证线程安全,但分布式下也是线程不安全的


@Autowired
private RedisTemplate redisTemplate;

@GetMapping("/lock")
public Result lock() {
    Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
    if (stock > 0) {
        stock = stock - 1;
        redisTemplate.opsForValue().set("stock", stock);
        System.out.println("剩余库存: " + stock);
    } else {
        System.out.println("扣减失败" + stock);
    }
    return Result.success("操作成功");
}

2、分布式锁、redis setnx

  • setnx
    • key 不存在,设置 key
    • key 存在,不做任何动作
  • redisTemplate.opsForValue().setIfAbsent
    • Key 不存在,设置成功。返回 true
    • Key 不存在,不操作。返回 false

@Autowired
private RedisTemplate redisTemplate;

@GetMapping("/lock")
public Result lock() {
    Boolean result = redisTemplate.opsForValue().setIfAbsent(LOCKKEY, "paulson");
    if (!result) {
        return Result.fail("分布式锁占用中");
    }


    Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
    if (stock > 0) {
        stock = stock - 1;
        redisTemplate.opsForValue().set("stock", stock);
        System.out.println("剩余库存: " + stock);
    } else {
        System.out.println("扣减失败" + stock);
    }


    redisTemplate.delete(LOCKKEY);
    return Result.success("操作成功");
}

注意:1、业务异常,没有删除 key 导致死锁
解决方案: try finally

3、分布式锁


@Autowired
private RedisTemplate redisTemplate;

@GetMapping("/lock")
public Result lock() {
    try {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(LOCKKEY, "paulson");
        if (!result) {
            return Result.fail("分布式锁占用中");
        }


        Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
        if (stock > 0) {
            stock = stock - 1;
            redisTemplate.opsForValue().set("stock", stock);
            System.out.println("剩余库存: " + stock);
        } else {
            System.out.println("扣减失败" + stock);
        }
    } finally{
        // 释放分布式锁
        redisTemplate.delete(LOCKKEY);
    }
    return Result.success("操作成功");
}

注意:服务器挂掉,重启,导致没有执行delete
解决方案:设置锁的生效时间

4、分布式锁

@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/lock")
public Result lock() {
    try {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(LOCKKEY, "paulson", 10, TimeUnit.SECONDS);
        if (!result) {
            return Result.fail("分布式锁占用中");
        }


        Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
        if (stock > 0) {
            stock = stock - 1;
            redisTemplate.opsForValue().set("stock", stock);
            System.out.println("剩余库存: " + stock);
        } else {
            System.out.println("扣减失败" + stock);
        }
    } finally  {
        // 释放分布式锁
        redisTemplate.delete(LOCKKEY);
    }
    return Result.success("操作成功");
}

注意:超时时间应该设置多久,还是执行结束就释放掉了
解决方案:只释放自己加的锁

5、分布式锁

 @Autowired
 private RedisTemplate redisTemplate;

 @Autowired
 private IdWorker idWorker;

@GetMapping("/lock")
public Result lock() {
    String clientId = idWorker.nextId() + "";
    try {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(LOCKKEY, clientId, 10, TimeUnit.SECONDS);
        if (!result) {
            return Result.fail("分布式锁占用中");
        }


        Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
        if (stock > 0) {
            stock = stock - 1;
            redisTemplate.opsForValue().set("stock", stock);
            System.out.println("剩余库存: " + stock);
        } else {
            System.out.println("扣减失败" + stock);
        }
    } finally {
        // 释放分布式锁,只释放自己创建的锁
        if (clientId.equals(redisTemplate.opsForValue().get(LOCKKEY))) {
            redisTemplate.delete(LOCKKEY);
        }
    }
    return Result.success("操作成功");
}

6、使用 redisson

  • Maven
<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.13.2</version>
</dependency>  
  • Gradle
compile 'org.redisson:redisson:3.13.2'  
  • 注入 Bean
@Bean
    public Redisson redisson() {
        // 单机模式
        Config config = new Config();
        config.useSingleServer().setAddress("redis://10.179.229.187:6379").setDatabase(1);
        return (Redisson) Redisson.create(config);
    }

@Autowired
private Redisson redisson;

@GetMapping("/deduct_lock2")
public Result redissonLock() {
    RLock lock = redisson.getLock(LOCKKEY);
    try {
        lock.tryLock(30, TimeUnit.SECONDS);
        Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
        if (stock > 0) {
            stock = stock - 1;
            redisTemplate.opsForValue().set("stock", stock);
            System.out.println("剩余库存: " + stock);
        } else {
            System.out.println("扣减失败" + stock);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        // 释放分布式锁,只释放自己创建的锁
        lock.unlock();
    }
    return Result.success("操作成功");
}

你可能感兴趣的:(Spring,java)