优惠券秒杀(三)

2023-11-02

L1296686146 冗谪 2023-07-28 13:24 发表于陕西

收录于合集#redis7个

优惠券秒杀一人一单

优惠券的目的是为了引流,但是目前的情况是一个人可以无限制的抢这个优惠券,因此,代码中应该添加一个用户只能下一单的逻辑。保证一个用户只能抢一张券,则只要保证该用户下的优惠券只要一张,即根据优惠卷id和用户id查询是否已经下过这个订单,如果能够查询到结果,则表明下过该订单,不能再次下单,否则表示没有下过单,进行下单操作。

图片

一人一单

存在问题:实现一人一单时,也存在线程安全问题,当多个线程携带的同一个用户id和同一个优惠券id时,此时该用户还没获取过该优惠券,则可能出现多线程问题,多个线程通过用户ID和优惠券ID可能查询不到订单,所以会有多个线程能够下单成功。

乐观锁适合更新数据,现在需要时插入数据,所有我们需要使用悲观锁操作。

@Override
public Long seckillVoucher(Long voucherId) {
    // 查询秒杀优惠券信息
    SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
    //判断秒杀是否开始和结束
    LocalDateTime beginTime = seckillVoucher.getBeginTime();
    LocalDateTime endTime = seckillVoucher.getEndTime();
    //如果当前时间 在开始时间之后 再结束时间之前 则表明秒杀能进行
    LocalDateTime localDateTime = LocalDateTime.now();
    if ( localDateTime.isBefore(beginTime) || localDateTime.isAfter(endTime) ){
        return null;
    }
    //获取库存量
    Integer stock = seckillVoucher.getStock();
    if (ObjectUtil.isNull(stock) || ObjectUtil.isNotNull(stock) && stock.intValue() <= 0){
        return null;
    }
    Long voucherOrder = createVoucherOrder(voucherId);
    return voucherOrder;
}

@Transactional
public synchronized Long createVoucherOrder(Long voucherId) {
    // 根据用户ID和优惠券ID 判断订单是否存在
    //获取用户ID
    Long id = UserHolder.getUser().getId();

    int count = this.count(new LambdaQueryWrapper<VoucherOrder>().eq(VoucherOrder::getUserId, id).eq(VoucherOrder::getVoucherId, voucherId));
    // 订单已经存在
    if (count > 0){
        return null;
    }

    //扣减库存 将现在库存和之前查询的库存做对比,如果一样,则表明没有人在此中间修改过库存,则认定线程安全,扣除库存
    boolean update = seckillVoucherService.update(
            new LambdaUpdateWrapper<SeckillVoucher>().setSql("stock = stock - 1").eq(SeckillVoucher::getVoucherId, voucherId)
                    .gt(SeckillVoucher::getStock, 0)
    );
    if (!update){
        return null;
    }
    //创建订单
    VoucherOrder voucherOrder = new VoucherOrder();
    //创建订单ID
    long orderId = redisIdWorker.nextId("order");
    voucherOrder.setId(orderId);

    voucherOrder.setUserId(id);
    // 代金券id
    voucherOrder.setVoucherId(voucherId);
    this.save(voucherOrder);
    return orderId;
}
  • 对悲观锁进行优化

    @Override
    public Long seckillVoucher(Long voucherId) {
        // 查询秒杀优惠券信息
        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
        //判断秒杀是否开始和结束
        LocalDateTime beginTime = seckillVoucher.getBeginTime();
        LocalDateTime endTime = seckillVoucher.getEndTime();
        //如果当前时间 在开始时间之后 再结束时间之前 则表明秒杀能进行
        LocalDateTime localDateTime = LocalDateTime.now();
        if ( localDateTime.isBefore(beginTime) || localDateTime.isAfter(endTime) ){
            return null;
        }
        //获取库存量
        Integer stock = seckillVoucher.getStock();
        if (ObjectUtil.isNull(stock) || ObjectUtil.isNotNull(stock) && stock.intValue() <= 0){
            return null;
        }
        Long id = UserHolder.getUser().getId();
    
        synchronized (id.toString().intern()){
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(id, voucherId);
        }
    }
    
    @Override
    @Transactional
    public  Long createVoucherOrder(Long userId, Long voucherId) {
        // 根据用户ID和优惠券ID 判断订单是否存在
        int count = this.count(new LambdaQueryWrapper<VoucherOrder>().eq(VoucherOrder::getUserId, userId).eq(VoucherOrder::getVoucherId, voucherId));
        // 订单已经存在
        if (count > 0){
            return null;
        }
        //扣减库存 将现在库存和之前查询的库存做对比,如果一样,则表明没有人在此中间修改过库存,则认定线程安全,扣除库存
        boolean update = seckillVoucherService.update(
                new LambdaUpdateWrapper<SeckillVoucher>().setSql("stock = stock - 1").eq(SeckillVoucher::getVoucherId, voucherId)
                        .gt(SeckillVoucher::getStock, 0)
        );
        if (!update){
            return null;
        }
        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //创建订单ID
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
    
        voucherOrder.setUserId(userId);
        // 代金券id
        voucherOrder.setVoucherId(voucherId);
        this.save(voucherOrder);
        return orderId;
    }
    
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>
    
    @EnableAspectJAutoProxy(exposeProxy = true)
    @MapperScan("com.hmdp.mapper")
    @SpringBootApplication
    public class HmDianPingApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HmDianPingApplication.class, args);
        }
    
    }
    

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优惠券秒杀(三) 的相关文章

  • 多维时序

    多维时序 MATLAB实现RBF径向基神经网络多变量时间序列未来多步预测 目录 多维时序 MATLAB实现RBF径向基神经网络多变量时间序列未来多步预测 预测效果 基本介绍 模型描述 程序设计 参考资料 预测效果 基本介绍 MATLAB实现
  • 你会使用GROUP BY吗?

    职业开始前两年 发现面试特别喜欢问SQL的问题 觉得不够高大上 觉得没有框架什么的高大上 但是当你处理实际的业务问题 发现SQL是避免不了的 SQL可以帮你解决一些你在代码上处理逻辑很麻烦的地方 即时现在有mybatis插件 可以不用写SQ

随机推荐

  • Stable Diffusion下载完身体有点吃不消(内附秋叶集合安装包)

    大家好 今天我来介绍一款令人惊叹的开源软件 Stable Diffusion 这是一款基于AI技术的图片生成软件 可以让用户轻松随心所欲地创建出各种令人惊叹的图片 在这里插入图片描述 Stable Diffusion拥有强大的AI算法 不管
  • 一文看懂随机森林 - Random Forest(附 4 个构造步骤+10 个优缺点)

    本文首发自 产品经理的人工智能知识库 原文地址 一文看懂随机森林 Random Forest 附 4 个构造步骤 10 个优缺点 随机森林是一种由决策树构成的集成算法 他在很多情况下都能有不错的表现 本文将介绍随机森林的基本概念 4 个构造
  • 外包公司“混”了2年,我只认真做了5件事,如今顺利拿到阿里 Offer。

    前言 是的 我一家外包公司工作了整整两年时间 在入职这家公司前 也就是两年前 我就开始规划了我自己的人生 所以在两年时间里 我并未懈怠 现如今 我已经跳槽到了阿里 顺利拿下 offer 自己的情况很普通 本科文凭 没有背景 分享这次我的经历
  • gd32f103 adc通道说明

    file main c brief ADC Temperature Vrefint demo Copyright 2017 GigaDevice 2017 06 23 V1 0 0 demo for GD32F30x include gd3
  • 深入理解Netty底层基础、中断、NIO、epoll~~~~

    深入理解Netty底层基础 LInux 操作系统中断 什么是系统中断 这个没啥可说的 大家都知道 CPU 在执行任务途中接收到中断请求 需要保存现场后去处理中断请求 保存现场称为中断处理程序 处理中断请求也就是唤醒对应的任务进程来持有CPU
  • 嵌入式软件—视频笔试记录

    文章目录 一 前言 二 笔试题目 1 C语言中的三种循环以及区别 2 以下代码循环几次 3 形参与实参定义以及函数形参为指针和指针引用方式的区别 4 链表的概念 链表与数组的区别 5 3 5 1 2的结果 6 以下程序运行结果是什么 7 i
  • 详解BP神经网络

    BackPropagation Neuron NetWok BP神经网络学习算法可以说是目前最成功的神经网络学习算法 显示任务中使用神经网络时 大多数是使用BP算法进行训练 在我看来BP神经网络就是一个 万能的模型 误差修正函数 每次根据训
  • Pre-Trained Models: Past, Present and Future综述总结(1)

    总体介绍 非神经模型 手工制作的特征和统计方法hand crafted features and statistical methods 神经模型 从数据中自动学习低维连续向量 也称为分布式表示 作为任务特定的特征 关键挑战之一是数据匮乏
  • golang该如何循环 多层结构体

    一 首先贴代码 你们大概看看就行 在看看后面的前端页面展示数据 你们就能理解 结构体嵌套的案例 package model import fmt time type Find struct Article Article SelectArt
  • 线程进程协程的实现代码

    单线程 import time def run print hello world time sleep 1 if name main for i in range 5 run 多线程 import threading import tim
  • 规范:前端代码开发规范

    一 前端静态代码检查工具 1 1 ESLint ESLint 是一个插件化的 JavaScript 代码检查工具 可以使用规则插件或者自定义规则对代码进行静态检查 1 2 JSLint JSLint 是由 Douglas Crockford
  • JAVA-static关键字 多态

    static Java中的static关键字主要用于内存管理 我们可以应用static关键字在变量 方法 块和嵌套类中 static关键字属于类 而不是类的实例 被static修饰的数据在内存中只会存在一份数据 所以这个数据适合用来共享数据
  • TypeError: write() argument must be str, not dict

    在写入文件的时候 出现 TypeError write argument must be str not dict 报错 可以使用json 格式写入 import requests import re import json def get
  • python bokeh_使用Bokeh在Python中进行交互式数据可视化

    python bokeh Bokeh prides itself on being a library for interactive data visualization Bokeh以成为交互式数据可视化的库而自豪 Unlike popu
  • 8051介绍--DW8051

    Overview synopsy公司设计的可综合内核IP 可嵌入到IC内部 它可以相容803x 805x单片机 1个机器周期 4个时钟周期 一条指令周期大概是2 9个机器周期 指令周期长度可变 因此可以分别访问高速和低速设备 MCU的clo
  • Call to undefined function mcrypt_get_block_size

    http stackoverflow com questions 17109818 install php mcrypt on centos 6 问题 Call to undefined function mcrypt get block
  • 【经验总结】tcp_tw_recycle参数引发的故障

    故障描述 2010年9月7日 新上线的手机游戏论坛有部分地区用户反应登陆游戏时出现不能登陆或登陆超时等情况 观察用户同时在线数量开始下降情况 排错过程 一 初步检查是否有变更导致的故障 1 联系同事检查网络是否有问题或有对该机房网络是否有进
  • Anaconda修改虚拟环境的安装位置

    1 首先试着新建一个环境 查看环境的安装路径 发现当前安装路径在C盘 我们的目标是改到D盘 2 输入conda info 查看当前的环境默认路径有哪几个 发现一共有三个 其中第一个是默认路径 现在的目标是将默认路径改到D盘 3 输入cond
  • 徐小明:上午涨的快,跌的也…

    我不喜欢炒股 今年股市估计少人欢喜 多人愁吧 老婆1万块都亏了4千多了 还不如到唐狮官方网站去买衣服tonlion hao9go com 哎 原文地址 徐小明 上午涨的快 跌的也快 作者 徐小明 徐小明 上午涨的快 跌的也快 今天上午低开之
  • 优惠券秒杀(三)

    L1296686146 冗谪 2023 07 28 13 24 发表于陕西 收录于合集 redis7个 优惠券秒杀一人一单 优惠券的目的是为了引流 但是目前的情况是一个人可以无限制的抢这个优惠券 因此 代码中应该添加一个用户只能下一单的逻辑