package com.itsoku.lesson004.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itsoku.lesson004.concurrencysafe.ConcurrencyFailException;
import com.itsoku.lesson004.concurrencysafe.DbConcurrencySafe;
import com.itsoku.lesson004.mapper.GoodsMapper;
import com.itsoku.lesson004.po.GoodsPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
/**
* <b>description</b>: Java高并发、微服务、性能优化实战案例100讲,,源码 & 文档 & 技术支持, <br>
* <b>time</b>:2024/3/26 21:03 <br>
* <b>author</b>:ready
*/
@Service
@Slf4j
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, GoodsPO> implements GoodsService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private DbConcurrencySafe dbConcurrencySafe;
@Autowired
private TransactionTemplate transactionTemplate;
/**
* 方案1:通过update中携带条件判断解决超卖问题
*
* @throws InterruptedException
*/
@Override
public void placeOrder1() throws InterruptedException {
//扣减库存的方法,返回值为1表示扣减库存成功,0表示失败
Function<String, Integer> reduceStock = (String goodsId) -> {
int update = goodsMapper.placeOrder1(goodsId, 1);
return update;
};
//模拟100人秒杀
this.concurrentPlaceOrderMock("方案1", reduceStock);
}
/**
* 方案2:乐观锁解决超卖问题
*
* @throws InterruptedException
*/
@Override
public void placeOrder2() throws InterruptedException {
//扣减库存的方法,返回值为1表示扣减库存成功,0表示失败
Function<String, Integer> reduceStock = (String goodsId) -> {
//1、先查询
GoodsPO goodsPO = this.getById(goodsId);
//2、判断库存是否==0,则直接返回失败
if (goodsPO.getNum() == 0) {
return 0;
}
//3.库存看起来够,但是并发的时候可能就不够了,下面带版本号更新库存,判断影响行数,update 为1表示成功,0表示扣减库存失败
int update = goodsMapper.placeOrder2(goodsId, 1, goodsPO.getVersion());
return update;
};
//模拟100人秒杀
this.concurrentPlaceOrderMock("方案2", reduceStock);
}
/**
* 方案3:对比数据修改前后是否和期望的一致,解决超卖问题
*
* @throws InterruptedException
*/
@Override
public void placeOrder3() throws InterruptedException {
//扣减库存的方法,返回值为1表示扣减库存成功,0表示失败
Function<String, Integer> reduceStock = (String goodsId) -> {
//1、根据商品id获商品
GoodsPO updateBeforeGoods = this.getById(goodsId);
//2、判断库存是否够
if (updateBeforeGoods.getNum() == 0) {
return 0;
}
log.info("库存:{}", updateBeforeGoods.getNum());
//启动事务操作扣减库存
int reduceStockResult = this.transactionTemplate.execute(action -> {
//3、执行更新扣减库存
this.goodsMapper.placeOrder3(goodsId, 1);
//4、修改数据完成后,查出来看一下,和期望的结果是不是一致的,如果是,表示成功,否则失败
GoodsPO updateAfterGoods = this.getById(goodsId);
//5、判断:库存扣减前的数量是否等于 扣减后库存数量+1,如果
if (updateBeforeGoods.getNum() - 1 != updateAfterGoods.getNum()) {
//设置事务回滚
action.setRollbackOnly();
return 0;
} else {
//成功
return 1;
}
});
return reduceStockResult;
};
//模拟100人秒杀
this.concurrentPlaceOrderMock("方案3", reduceStock);
}
/**
* 方案4:通过辅助类解决超卖问题,这种本质上可以解决所有并发修改db数据出错的问题
*
* @throws InterruptedException
*/
@Override
public void placeOrder4() throws InterruptedException {
//扣减库存的方法,返回值为1表示扣减库存成功,0表示失败
Function<String, Integer> reduceStock = (String goodsId) -> {
try {
//使用 dbConcurrencySafe.exec 包住需要并发操作的数据,可以确保数据修改的安全性
return this.dbConcurrencySafe.exec(GoodsPO.class, goodsId, () -> {
//1、根据商品id获商品
GoodsPO goodsPO = this.getById(goodsId);
//2、判断库存是否够
if (goodsPO.getNum() == 0) {
return 0;
}
//3、执行更新扣减库存
this.goodsMapper.placeOrder3(goodsId, 1);
return 1;
});
} catch (Exception e) {
return 0;
}
};
//模拟100人秒杀
this.concurrentPlaceOrderMock("方案4", reduceStock);
}
/**
* 模拟100人秒杀请求
*
* @param fun 扣减库存的函数,fun函数的参数为商品id,返回值:1:表示抢购成功,0:表示抢购失败
* @throws InterruptedException
*/
private void concurrentPlaceOrderMock(String method, Function<String, Integer> fun) throws InterruptedException {
//1、初始化一条商品记录 [商品Id:1,名称:iphone、库存:10]
String goodsId = "1", goodsName = "iphone";
// 库存10个
int num = 10;
GoodsPO goodsStart = this.initTestData(goodsId, goodsName, num);
//2、创建线程池,大小为100,模拟100个线程并发下单
int concurrentNum = 100;
ExecutorService executorService = Executors.newFixedThreadPool(concurrentNum);
CountDownLatch countDownLatch = new CountDownLatch(concurrentNum);
AtomicInteger successNum = new AtomicInteger(0);
AtomicInteger failNum = new AtomicInteger(0);
//使用线程池模拟100人抢购
for (int i = 0; i < concurrentNum; i++) {
executorService.execute(() -> {
try {
// 调用抢购的函数,update表示抢购的结果,1:表示抢购成功,0:抢购失败
int update = fun.apply(goodsId);
//update=0,表示更新失败,否则表示更新成功
if (update == 0) {
//抢购失败,失败次数+1,业务上在这里可能要做一些事情(如让事务回滚,那么可以在此抛出一个异常,spring事务会自动回滚)
failNum.incrementAndGet();
} else {
//更新成功,成功次数+1
successNum.incrementAndGet();
}
} finally {
countDownLatch.countDown();
}
});
}
// 调用下面的方法,等待上面线程池中的任务都执行完毕后,才会继续向下走
countDownLatch.await();
// 模拟100人抢购结束,获取抢购完毕后商品的信息
GoodsPO goodsEnd = this.getById(goodsId);
//抢购完毕,输出日志,方便
蔡定努
- 粉丝: 1w+
- 资源: 65
最新资源
- 基于51单片机的密码锁 门禁系统仿真设计 实现功能: 1、密码6位,初始密码123456 2、按下按键,带有“滴”按键提示音 3、输入密码后,密码由“数字”变为“*”号 4、其余功能见下图使用说明
- 基于51单片机的串口控制系统仿真设计 实现功能: 1、 将温湿度传感器(DHT11)采集到的数据实时显示在lcd上,并通过串口显示 2、可通过串口远程控制继电器和LED的开关 PS:通过串口助手发送命
- 基于51单片机的波形发生器系统仿真设计 其它仿真设计也可加好友 实现功能: 1、通过按键切波形 可输出正弦波 方波 三角波 锯齿波 组合波(正弦波+三角波+锯齿波) 梯形波 2、切波形时,数码管显示
- 三菱自动机、自动卖机 GX Work2程序和GT Designer3程序 功能: 1、可以买5种产 2、投大于等于价格时对应的才可以 3、选择的后自动扣 4、按 币键自动金额自动清零 00
- abaqus粗糙表面随机分布建模,随机粗糙表面,高斯分布,Step通用格式
- 西门子1200和多台smart PN 通讯案例 网上西门子1200和200smart pn通讯例程都是一台smart从站,都没有讲多台从站时的配置和编程方法 本案例展示了一台1200PLC和
- 基于8086 微机原理的计算器系统仿真设计 实现功能: 1、实现加减乘除运算,并通过四位一体数码管显示 2、清零功能 包含仿真+源码 仿真软件:Proteus8.9 编程软件:Masm for Win
- 西门子S7-1500暖通空调制药厂洁净空调PLC程序案例,硬件采用西门子1500CPU+ET200SP接口IO模块,HMI采用西门子触摸屏 具体为制药厂BMS(洁净空调自控系统)医药洁净室程序,程
- 变压器温度检测系统温度报警器 1.kealc编程 2.protues仿真 3.绘图AD 要求该系统能够实时检测变压器顶层油温和绕组温度,温度超限时报警,并能实时显示当前温度值 顶层油温度规定限值:对
- 威纶通触摸屏与两台台达变频器modbus rtu通讯程序 触摸屏为mt6103ip,变频器为VFD-M,用的在线模拟,真实触摸屏只需修改com口
- 三相级联H桥逆变器仿真模型,七电平,十一电平逆变器,采用载波移相或者载波层叠的控制方法,可以提供参考文献
- 单相pfc升压斩波电路仿真,交流电源经过不控整流再经过boost升压,输出直流400v 电压闭环pi控制,含功率因数测量部分
- 单相交交变频电路仿真,负载为阻感负载,文件中附带理论说明 仿真为自己搭建,不懂得地方可以咨询讲解,便于自学和理解交交变频电路的原理 仿真中包含输出电压的傅立叶分析,可以改变负载 默认发matl
- 酒精浓度检测器 可带报告,带 proteus仿真,带keil源程序 1、根据所设计目的设置可调节的酒精浓度检测器,并通过硬件软件系统将检测的酒精浓度反应到LCD显示屏上; 2、可通过按键实现报警浓
- 钢铁厂电除尘控制系统上位机画面+博途plc程序+触摸屏画面的完整项目文件,附带eplan图纸,实际运行的项目,wincc7.5版本,博途V16,都采用结构化编程,是学习wincc画面组态和博途编程及触
- 基于51单片机的智能家居控制系统仿真设计 环境监测 实现功能: 1、通过按键可设置温湿度数据的阈值上下限,设置烟雾浓度的阈值上限 2、将温湿度传感器(DHT11)的数据实时显示在LCD上 当温湿度数
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈