优惠券秒杀(三)

news2024/9/30 5:27:39

优惠券秒杀一人一单

优惠券的目的是为了引流,但是目前的情况是一个人可以无限制的抢这个优惠券,因此,代码中应该添加一个用户只能下一单的逻辑。保证一个用户只能抢一张券,则只要保证该用户下的优惠券只要一张,即根据优惠卷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.classargs);
        }

    }

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/803241.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

List<List<>> 转 List lambda,List<HashMap<>>转List<>

1、在实际的业务处理中&#xff0c;我们经常会碰到需要合并同一个集合内相同属性对象的情况&#xff0c;比如&#xff0c;同一个用户短时间内下的订单&#xff0c;我们需要将各个订单的金额合并成一个总金额。那么用lambda表达式和HashMap怎么分别处理该种情况呢&#xff1f;本…

vue3如何设置全局过滤器、app.config.globalProperties如何使用配置

vue3 相比于 vue2 取消了很多的API&#xff0c; filter就在其中&#xff0c;但是我们可以使用其他方法替代vue2中的filter 通过 app.config.globalProperties 来注册一个全局都能访问到的属性 我们再来说说 app.config.globalProperties 是什么&#xff0c;如何使用&#xff…

httpd升级2.4.57及后续问题处理

文章目录 背景信息操作步骤构建rpm包升级httpd启动httpd报错及处理缺少mod_systemd缺少mod_cgi 相关文件 背景信息 由于2.4.37版本httpd有安全漏洞&#xff0c;需要升级到2.4.57版本&#xff0c;2.4.57版本是当前最新的版本&#xff0c;只具备源码包&#xff0c;不具备rpm包&a…

Quartz 调度原理与源码分析

文章目录 一、Quartz基础1、入门案例 二、获取调度器实例源码分析1、读取配置文件&#xff1a;initialize()2、初始化工作&#xff1a;instantiate()&#xff08;1&#xff09;创建线程池&#xff08;包工头&#xff09;SimpleThreadPool&#xff08;2&#xff09;WorkerThread…

十五章:使用类别峰值响应的弱监督实例分割

0.摘要 目前&#xff0c;使用图像级别标签而不是昂贵的像素级掩码进行弱监督实例分割的研究还未得到充分探索。本文通过利用类别峰值响应来实现一个分类网络&#xff0c;用于提取实例掩码&#xff0c;来解决这个具有挑战性的问题。只通过图像标签的监督下&#xff0c;完全卷积的…

winform 将resources资源文件反编译为resx文件

resources资源文件反编译为resx文件 【前景提要】 在日常工作中我们会遇到需要将一个编译后的winform程序反编译出其对应的源码&#xff0c;然而在常用的反编译工具中对于项目中使用的资源文件是编译为resources文件的&#xff0c;这个资源文件在反编译后的源码中是无法直接使用…

MES管理系统中设备管理功能的原理是什么

制造执行系统MES是一种应用于制造工厂的实际操作系统&#xff0c;它通过实时监控和控制生产流程&#xff0c;为生产过程提供全面的管理和优化。在MES管理系统解决方案中&#xff0c;设备管理功能是非常重要的一部分&#xff0c;它可以实现设备实时监控、故障预警、维护保养等功…

MyBatisPlus从入门到精通-2

接着上一讲的Mp的分页功能 下面我们讲解条件查询功能和其他功能 解决一下日志输出和banner问题 每次卞就会输出这些日志 很不美观&#xff0c;现在我们关闭一下 这样建个xml&#xff0c;文件名为logback.xml 文件内容改成这样 配置了logback但是里面什么都没写就不会说有日…

视频监控汇聚平台EasyCVR向上级联时,上级一直回复401是什么原因?

视频监控管理EasyCVR视频融合平台基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;具体包括&#xff1a;NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。平台具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环…

下载文件出错:org.apache.catalina.connector.ClientAbortException

解决方案 复现步骤&#xff1a; 浏览器调整下载速度后&#xff0c;超过1分钟的下载会自动断开&#xff0c;调整connectionTimeout后&#xff0c;问题解决。

前端Vue入门-day04-用vue实现组件通信

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 组件的三大组成部分 注意点说明 组件的样式冲突 scoped data 是一个函数 组件通信 什么是组件通信 不…

图注意力网络论文详解和PyTorch实现

图神经网络(gnn)是一类功能强大的神经网络&#xff0c;它对图结构数据进行操作。它们通过从节点的局部邻域聚合信息来学习节点表示(嵌入)。这个概念在图表示学习文献中被称为“消息传递”。 消息(嵌入)通过多个GNN层在图中的节点之间传递。每个节点聚合来自其邻居的消息以更新其…

特殊矩阵的压缩存储

1 数组的存储结构 1.1 一维数组 各数组元素大小相同&#xff0c;且物理上连续存放。第i个元素的地址位置是&#xff1a;a[i] LOC i*sizeof(ElemType) (LOC为起始地址) 1.2 二维数组 对于多维数组有行优先、列优先的存储方法 行优先&#xff1a;先行后列&#xff0c;先存储…

C# 汇总区间

228 汇总区间 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖&#xff0c;并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中的每个区间范围…

Mybatis中where 1=1 浅析

在一些集成mybatis的工程中经常看到where11 的代码&#xff0c;也有同事问我&#xff0c;这样写有什么用&#xff0c;下面对其进行简单的分析记录一下。 1、场景 看下面这样一段xml中的代码 <select id"queryBook" parameterType"com.platform.entity.Book…

【JavaEE】博客系统前后端交互

目录 一、准备工作 二、数据库的表设计 三、封装JDBC数据库操作 1、创建数据表对应的实体类 2、封装增删改查操作 四、前后端交互逻辑的实现 1、博客列表页 1.1、展示博客列表 1.2、博客详情页 1.3、登录页面 1.4、强制要求用户登录&#xff0c;检查用户的登录状态 …

Jenkins构建完成后发送消息至钉钉

钉钉群的最终效果&#xff1a; 1、jenkins安装DingTalk插件&#xff0c;安装完成后重启 2、配置钉钉插件 参考官网文档&#xff1a;快速开始 | 钉钉机器人插件 系统管理 拉到最下面&#xff0c;可以看到钉钉配置 按照如下配置钉钉机器人 配置完成可以点击测试按钮&#xff0…

css 书写规范!其他人总结!

CSS书写顺序 1.位置属性(position, top, right, z-index, display, float等) 2.大小(width, height, padding, margin) 3.文字系列(font, line-height, letter-spacing, color- text-align等) 4.背景(background, border等) 5.其他(animation, transition等) CSS书写规范 使用…

利用官网文档快速上手 Android 开发

官网学习链接&#xff1a;官网链接 官网教程

Vue结合echarts实现水滴图

效果展示 核心代码 <template><div id"cpu" style"width: 270px;height: 200px;"></div> </template><script>import * as echarts from echarts;export default {name: "show",methods:{aucDrawLine() {// 基于…