秒杀实现通过乐观锁控制超卖问题

通过悲观锁控制每个用户只能下一单,避免用户多次点击,发送的多次下单请求(即多个线程)都成功,避免恶意攻击

                        每个请求访问Tomcat时,就会分配一个线程处理请求

业务逻辑:

注*以下逻辑中报错也可以改为给前端返回错误信息

1.检查数据库查看库存是否>0,不满足直接报错

2.当数据库库存>0,检查该用户对本商品的订单是否存在,如果已存在说明下过单,报错

3.如果订单也不存在,对数据库数据进行减库存,这里容易出现超卖,使用乐观锁

4.减库存后创建订单,这个过程和2步骤有关联,应该使用悲观锁控制2-4步骤的访问

乐观锁(本质sql的where语句验证):即修改数据库前查看数据前后是否一致,不一致说明中间有其他线程修改数据,则放弃修改。

update product set sale=slae-1 where sale=?
//?为检查数据库库存是否>0时查询的值,这样在修改数据时通过where在验证一遍数据是否被修改

缺点:高并发场景下修改数据库成功率太低!

优化:宽松的乐观锁,严格来说不算乐观锁了,就是条件更改

update product set sale=slae-1 where sale>0

将条件变为sale>0哪怕中间有其他线程进行减库存,只要数量依然>0依然允许修改,这样就可以完美符合我们减库存的预期(可以使用Jmeter工具进行高并发测试)

悲观锁(本质synchronized同步机制):避免同一个用户对创建订单接口访问进行多次请求时,多次请求都创建订单成功。如果不是同一用户,允许异步访问数据库创建多个不同用户订单

1.应该将2-4步骤抽离出单独放到一个方法里面,因为减库存和创建订单是一个事务,应该将其放到同一事务中执行

2.同步锁不建议直接加到2-4步骤的方法上面,因为方法同步锁,锁对象都是this,如果该类中有多个锁的对象都是this,容易照成线程的过度阻塞,导致程序反应变慢

所以对象锁不应该绑定this而是和userID绑定,因此就要使用同步代码块进行上锁,锁对象都为和userID挂钩的同一种对象即可-----这里使用userID.toString().intern()来指定锁对象

2.1为什么使用userID.toString().intern()来充当锁对象:

如果直接使用Integer类型的userID参数对象,来作为锁对象毫无意义,因为不同请求传递的参数都会在堆中new出不同的对象,这样哪怕是多个线程(请求)访问接口,传递的同一userID同步锁也无法限制他们进行同步访问代码块,因为锁对象根本不是同一对象

直接使用this也不行,因为我们只需要限制参数为同一userID的请求访问代码块时要同步,不同userID的请求之间不需要使用同一把锁限制他们。而是使用多把锁,限制同一userID的请求进行同步访问代码块2-4步骤,多把锁允许不同userID的请求可以异步访问访问代码块2-4步骤。如果使用了this,所有请求访问代码块2-4步骤时都会同步访问,不符合我们预期

3.这个同步代码块不应该封装到2-4步骤的事务方法中去,而是封装到调用该方法的位置

如果封装到2-4步骤的事务方法中,同一userID线程虽然访问代码块被同步限制了,但是除了代码块后事务还没有真正提交,这时其他同一userID的线程又进入代码块中,查询数据库中有无订单时,可能没有订单,因为方法没执行完毕,事务未提交,数据库中还没有订单信息。

所以为了避免这种情况,代码块应该封装到调用该事务方法的地方,将该事务方法放到代码块中执行

4.@Transaction事务失效

上面我们说应该在其他方法中调用2-4步骤的事务方法,但是同一类中调用事务方法,事务不会生效,只有通过spring创建的代理对象,引用事务方法时事务才会生效.

所以在引用事务方法时要通过spring框架的ApplicationContext对象的getBean(类名.class)来获取代理对象调用事务方法,这样事务才会生效!!

代码实现:(仅作参考,结合业务逻辑食用)

    @AutowiredApplicationContext context;//模仿秒杀减库存,创建订单@Overridepublic Boolean killInSecond(Integer userID,Integer productID){//检查库存是否>0Product product = pm.selectByPrimaryKey(productID);if(product.getSales()<=0){throw new MyExceptionHandler("库存不足");}//调用2-4步骤方法Boolean result=false;synchronized (userID.toString().intern()){//使用代理对象调用事务方法ProductServiceImpl bean = context.getBean(ProductServiceImpl.class);result=bean.ProductAndOrder(userID,productID);}return result;}@AutowiredOrderMapper om;@AutowiredRedisIdIncrement redisId;//redis全局唯一ID生成工具,想省事可以直接uuid//创建订单,减库存操作@Transactionalpublic Boolean ProductAndOrder(Integer userID,Integer productID){//检查数据库中书否存在该用户订单Integer orderCount = om.selectOrderByUserIdAndProductId(userID, productID);if(orderCount>0){throw new MyExceptionHandler("用户已下单");}//订单不存在减库存,宽松乐观锁Integer result = pm.updateProductBysale(productID);if(result!=1){throw new MyExceptionHandler("库存不足");}//创建订单//获取redis唯一IDLong orderId = redisId.getRedisID("order");//封装订单Order order=new Order(orderId.toString(),userID,"","",productID,"",null,1,0,null,null,null,null,new BigDecimal(100));result = om.insertCompleteOrder(order);if(result!=1){return false;}return true;}

mybatisXML文件的乐观锁sql语句

<!--  对单个商品减库存--><update id="updateProductBysale">update product set sales=sales-1 where id=#{productId} and sales>0</update>

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go与Python爬虫实战对比:从开发效率到性能瓶颈的深度解析

目录 引言&#xff1a;两种语言&#xff0c;两种哲学 开发效率对比&#xff1a;从框架设计看易用性 Python的"开箱即用" Go的"手动组装" 性能对比&#xff1a;从并发模型看效率差异 理论性能对比 实际测试数据 错误处理对比&#xff1a;从编程范式…

初识c语言————排序方法

今天我们学习的是c语言中的排序方法目录&#xff1a;一.冒泡排序法二.选择排序法下面我们正式学习c语言中的排序方法一.冒泡排序法1.冒泡排序法的过程&#xff1a;将无序的数组通过数组之间的大小比较&#xff0c;排成有序的样子2.例如&#xff1a;5&#xff0c;3&#xff0c;4…

爬虫与数据分析结合案例:中国大学排名爬取与分析全流程

爬虫与数据分析结合案例&#xff1a;中国大学排名爬取与分析全流程 一、案例背景与目标 本案例以高三网中国大学排名&#xff08;网址&#xff1a;2021中国的大学排名一览表_高三网&#xff09;为数据源&#xff0c;完成从数据爬取到分析可视化的全流程实践。主要目标包括&am…

行业分享丨SimSolid 在汽车零部件开发中应用的可行性调研及实践

*本文源自汽车行业用户范会超投稿1、背景车型短周期开发背景下&#xff0c;高效的仿真技术显得尤为重要。Altair 推出了多款加速设计/仿真的软件&#xff0c;其中无网格软件 SimSolid 与业务有一定的契合度&#xff0c;有必要论证其在汽车零部件结构分析领域的可行性。2、目标评…

MacOS字体看起来比在 Windows 上更好?

字体控们注意啦&#xff01;&#x1f389;你们有没有发现&#xff0c;同样一段文字&#xff0c;在Mac和Windows上看起来就是不一样&#xff1f;Mac上的字仿佛自带柔光滤镜&#xff0c;圆润又舒适&#xff1b;而Windows上的字则像是精心雕琢的刀锋&#xff0c;锐利且清晰。这背后…

Torch -- 卷积学习day1 -- 卷积层,池化层

目录 一、CNN概述 二、卷积层 1、卷积核 2、卷积计算 3、边缘填充 4、步长 5、多通道卷积计算 6、多卷积核卷积计算 7、特征图大小 8、卷积参数共享 9、局部特征提取 10、卷积层API 三、池化层 1、池化层概述 1.池化层的作用 2.池化层类型 2、池化层计算 3、步…

蓝桥杯---第六届省赛单片机组真题

先出手写的代码&#xff0c;代码分析还需要一段时间&#xff0c;不难&#xff0c;大家认真写。#include <STC15F2K60S2.H> #include "Seg.h" #include "LED.h" #include "Key.h" #include "DS1302.h" #include "DS18B20.h&…

GPT-5深度解析:精准、高效、务实的新一代AI引擎

&#x1f31f; GPT-5深度解析&#xff1a;精准、高效、务实的新一代AI引擎在万众瞩目中&#xff0c;OpenAI于2025年8月7日正式推出GPT-5——这一代模型没有华丽的创意革命&#xff0c;却以惊人的准确率提升、断崖式降价和强大的工程能力&#xff0c;悄然重塑了生成式AI的应用边…

oss(阿里云)前端直传

WEB端前端直传 参考文档&#xff1a;web前端直传并设置上传回调 封装oss-upload.ts // 图片上传 import { uploadToken } from /api/uploadFile.js // 获取oss token接口// 定义 OSS 信息类型 interface OssInfo {policy: string;signature: string;x_oss_credential: strin…

vscode uv 发布一个python包:编辑、调试与相对路径导包

背景 最近一直在使用uv做python包管理&#xff0c;用起来很方便。 尤其是在代码上传到github的时候&#xff0c;pyproject.toml 会显示出当前项目依赖的python包。这样在把代码下载到本地之后&#xff0c;直接uv sync就可以很方便地恢复出python环境。 uv 除了有上述优点&…

Secure 第四天作业

实验需求&#xff1a;需求一拓扑&#xff1a;按照以上拓扑所示&#xff0c;完成以下需求&#xff1a;参考以上拓扑&#xff0c;配置设备IP地址&#xff0c;使用UNL里Secure第四天拓扑即可。&#xff08;有兴趣的同学课后也可按照PPT原拓扑做做实验&#xff09;&#xff1b;配置…

利用开漏输出模式模拟IIC

/************************************************************利用IO口模拟IIC时序&#xff0c;需要使用2个IO口(SDA和SCL)SCL时钟线只能由主器件进行控制&#xff0c;所以SCL引脚必须为输出模式SDA数据线&#xff0c;在主器件发送数据时&#xff0c;SDA引脚为输出模式SDA数…

闸机控制系统从设计到实现全解析:第 5 篇:RabbitMQ 消息队列与闸机通信设计

第 5 篇&#xff1a;RabbitMQ 消息队列与闸机通信设计RabbitMQ 是一款开源的消息队列中间件&#xff08;Message Queue&#xff0c;MQ&#xff09;&#xff0c;基于 Erlang 语言开发&#xff0c;遵循 AMQP&#xff08;Advanced Message Queuing Protocol&#xff0c;高级消息队…

Linux 常用命令大全:覆盖日常 99% 操作需求

1、基本命令 pwd&#xff1a;显示当前工作目录的绝对路径&#xff0c;例如在复杂目录结构中快速确认位置&#xff0c;执行后会输出类似/home/user/documents的结果。 cd&#xff1a;切换目录&#xff0c;cd 目录路径可进入指定目录&#xff0c;cd ~回到当前用户的家目录&…

普通电脑与云电脑的区别有哪些?全面科普

近年来&#xff0c;越来越多的人不再购置升级自己的电脑&#xff0c;转而选择云电脑&#xff0c;云端产品正在变得越来越普及易用。那么它究竟跟我们的普通本地设备有什么区别呐&#xff1f;或许很多人并不知悉&#xff0c;对此&#xff0c;本篇内容小编就为大家简要科普一下普…

【Python】支持向量机SVM

示例代码&#xff1a;import numpy as np import matplotlib.pyplot as plt from sklearn import svm from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report# 设…

当AI学会“抄近路”:残差网络如何突破深度学习的极限

**——解读《Deep Residual Learning for Image Recognition》**今天我想带大家回到2015年&#xff0c;见证人工智能领域的一场“捷径革命”——由何恺明等人提出的**深度残差学习框架&#xff08;ResNet&#xff09;**。这篇论文解决了困扰AI界多年的“深度诅咒”&#xff0c;…

HCIP--BGP综合实验

目录 BGP综合实验报告 一、实验拓扑 二、实验要求 三、实验思路 &#xff08;一&#xff09;IP地址规划 &#xff08;二&#xff09;整体思路 四、实验步骤 &#xff08;一&#xff09; IP地址配置 &#xff08;二&#xff09; AS2内部配置OSPF协议 &#xff08;三&a…

Java 基础编程案例:从输入交互到逻辑处理

在Java编程学习中&#xff0c;输入输出、循环控制和逻辑判断是核心基础。本文整理了10个经典案例&#xff0c;涵盖Scanner输入处理、斐波那契数列、成绩统计、登录验证等场景&#xff0c;帮助初学者掌握编程逻辑与实用技巧。 一、Scanner输入交互&#xff1a;获取用户输入并处理…

LeetCode 面试经典 150_数组/字符串_整数转罗马数字(18_12_C++_中等)(模拟)(对各位进行拆解)

LeetCode 面试经典 150_数组/字符串_整数转罗马数字&#xff08;18_12_C_中等&#xff09;题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;模拟&#xff09;&#xff1a;思路二&#xff08;对各位进行拆解&#xff09;&a…