高并发代码设计(redis)-暂时把redis当作数据库使用

1.没有并发的场景

@RequestMapping("/test")
    public String deductStock() throws InterruptedException {
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        if(stock > 0){
            // 如果库存大于0
            Integer realStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock" , realStock.toString());
            System.out.println("扣除成功,剩余:" + realStock);
        } else {
            System.out.println("扣除失败,不足");
        }
        return "success";
    }

2.存在并发的场景(加jvm级别的锁)-单点的时候可用

@RequestMapping("/test")
    public String deductStock() throws InterruptedException {
        synchronized (this){
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                // 如果库存大于0
                Integer realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock" , realStock.toString());
                System.out.println("扣除成功,剩余:" + realStock);
            } else {
                System.out.println("扣除失败,不足");
            }
            return "success";
        }
    }

3.存在并发的场景(在redis层面进行加锁-利用redis单线程内存模型)-集群的时候---基本可用

@RequestMapping("/test")
    public String deductStock() throws InterruptedException {
        // setnx(防止key重复被覆盖数据) ; try-finally释放锁(防止执行过程中异常导致死锁,不能防止宕机的场景) ;加过期时间、原子化 防止宕机(防止死锁);
        String lockkey = "lock";
        try {
            // 高版本可用
            // Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, "zmc", 30L, TimeUnit.SECONDS);

            // 不保证原子性
            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, "zmc");
            stringRedisTemplate.expire(lockkey, 30, TimeUnit.SECONDS);
            if(!result){
                return "error";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                // 如果库存大于0
                Integer realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock" , realStock.toString());
                System.out.println("扣除成功,剩余:" + realStock);
            } else {
                System.out.println("扣除失败,不足");
            }

        } finally {
            stringRedisTemplate.delete(lockkey);
        }
        return "success";

    }

4.一般高并发的场景(失效时间设置不合理:锁失效了代码还没执行完毕,finally中的delete会干掉其他线程的锁,可能导致所有的锁永久失效)(有可能超时:代码每执行完就锁过期了)

@RequestMapping("/test")
    public String deductStock() throws InterruptedException {
        // setnx(防止key重复被覆盖数据) ; try-finally释放锁(防止执行过程中异常导致死锁,不能防止宕机的场景) ;加过期时间、原子化 防止宕机(防止死锁);
        String lockkey = "lock";
        // 用于:每个线程只释放自己的锁
        String clientId = UUID.randomUUID().toString();
        try {
            // 高版本可用
            // Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, "zmc", 30L, TimeUnit.SECONDS);
            // 不保证原子性
            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, "zmc");
            stringRedisTemplate.expire(lockkey, 30, TimeUnit.SECONDS);
            if(!result){
                return "error";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                // 如果库存大于0
                Integer realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock" , realStock.toString());
                System.out.println("扣除成功,剩余:" + realStock);
            } else {
                System.out.println("扣除失败,不足");
            }

        } finally {
            if(clientId.equals(stringRedisTemplate.opsForValue().get(lockkey))){
                stringRedisTemplate.delete(lockkey);
            }
        }
        return "success";

    }

5.锁的时间设置不合理,因为不可预料所以不可能设置合理,所以需要有一个机制-每到锁的过期时间三分之一的时间的时候,重新设置过期时间

使用redission

@RequestMapping("/test")
    public String deductStock() throws InterruptedException {
        // setnx(防止key重复被覆盖数据) ; try-finally释放锁(防止执行过程中异常导致死锁,不能防止宕机的场景) ;加过期时间、原子化 防止宕机(防止死锁);
        String lockkey = "lock";
        // 用于:每个线程只释放自己的锁
        String clientId = UUID.randomUUID().toString();
        RLock redissonLock = redisson.getLock(lockkey);
        try {
            redissonLock.lock(30, TimeUnit.SECONDS);
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                // 如果库存大于0
                Integer realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock" , realStock.toString());
                System.out.println("扣除成功,剩余:" + realStock);
            } else {
                System.out.println("扣除失败,不足");
            }

        } finally {
            redissonLock.unlock();
        }
        return "success";

    }
原创文章 75 获赞 28 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_33999844/article/details/98766977