记录使用Redis分布式锁、悲观锁、synchronized防止超卖问题

在网上看到使用一些锁机制保证高并发情况,商品超卖的问题,我就自己写了个demo。

前置条件:建议新建一个springboot工程(添加web依赖),然后自行整合mybatisplus、redis,可以参照以下链接:

mybatisPlus:https://blog.csdn.net/wang20000102/article/details/132615071

redis:https://blog.csdn.net/lwj_07/article/details/126265935。就只需要引入依赖和添加redis配置

1、使用redis当分布式锁:

 核心代码如下:

controller层

 service

 serviceImpl

@Override
@Transactional
public void testConcurrent() {
    //使用redis实现分布式锁setnx
    String CHANGE_DEMO = "change_demo";
    Boolean flag = redisTemplate.opsForValue().setIfAbsent(CHANGE_DEMO, Thread.currentThread().getId(), 5, TimeUnit.SECONDS);
    //如果拿到了锁,执行业务逻辑
    if (flag) {
        try {
            log.info("线程{}拿到锁,开始执行业务逻辑",Thread.currentThread().getId());
            Device device = deviceMapper.selectById(3);
            //锁过期时间是5秒  这里我让线程休眠5s  那键值肯定就是过期了 看看有什么问题
            //Thread.sleep(6000);
            if (device.getSortNum() > 0) {
                log.info("当前库存是:{}", device.getSortNum());
                int i = device.getSortNum() - 1;
                device.setSortNum(i);
                Integer integer = deviceMapper.updateById(device);
                Device select = deviceMapper.selectById(3);
                log.info("线程{}抢到商品,当前减后库存是:{}", Thread.currentThread().getId(), select.getSortNum());
            }
        }catch (Exception e){
            log.info("扣除库存中出现异常",e);
        }finally {
            //谁加锁 谁解锁
            if (Objects.equals(redisTemplate.opsForValue().get(CHANGE_DEMO),Thread.currentThread().getId())){
                redisTemplate.delete(CHANGE_DEMO);
                log.info("线程{}解锁成功",Thread.currentThread().getId());
            }
        }
    }
}

 


配置文件application.yaml

 数据库表:

使用ApiFox 进行并发请求:

 

 控制台打印:因为数据库是3,所以会有三次扣减库存动作

 

参照如下链接:https://blog.csdn.net/weixin_44130574/article/details/125678000

2、使用悲观锁防止超卖:

 代码

//先查库存
Device device = deviceMapper.selectByIdUpdate(3);  // 核心为该行代码
//Thread.sleep(3000);
if (device.getSortNum() > 0) {
    System.out.println("当前库存是:" + device.getSortNum());
    int i = device.getSortNum() - 1;
    device.setSortNum(i);
    Integer integer = deviceMapper.updateById(device);
    Device device1 = deviceMapper.selectById(3);
    System.out.println("抢到商品,当前减后库存是:" + device1.getSortNum());
    System.out.println("成功");
} else {
    System.out.println("当前没抢到线程是:" + Thread.currentThread().getName());
    System.out.println("没有库存了");
}

 

  其实只是在第一行查询那添加一个for update进行行锁的控制

 可参照链接:https://blog.csdn.net/cxclll/article/details/130602547

3、使用synchronized就行加锁

 代码如下:

 

synchronized (this) {
    //先查库存
    Device device = deviceMapper.selectById(3);
    //Thread.sleep(3000);
    if (device.getSortNum() > 0) {
        System.out.println("当前库存是:" + device.getSortNum());
        int i = device.getSortNum() - 1;
        device.setSortNum(i);
        Integer integer = deviceMapper.updateById(device);
        Device device1 = deviceMapper.selectById(3);
        System.out.println("抢到商品,当前减后库存是:" + device1.getSortNum());
        System.out.println("成功");
    } else {
        System.out.println("当前没抢到线程是:" + Thread.currentThread().getName());
        System.out.println("没有库存了");
    }
}

注意:使用synchronized不能跟声明式事务@Transactional注解一起使用,原因是声明式事务使用aop处理事务问题,会出现锁释放了但是事务没提交导致锁失效的问题

可参照链接:https://blog.csdn.net/cxclll/article/details/130602547 

4、也可使用乐观锁控制

 

 

 

 

 

注:不是抄袭,我只是把代码自行实现下,觉得作者写的很好。如有侵权,联系我删除文章。

 

posted @ 2024-01-22 14:50  多多指教~  阅读(32)  评论(0编辑  收藏  举报