当前位置: 首页 > wzjs >正文

好网站你知道线上推广的渠道和方法

好网站你知道,线上推广的渠道和方法,新莱芜网,怎么做网站相册今天在学习黑马程序员的黑马点评实战项目中关于优惠券秒杀一人一单的时候碰到了一些问题&#xff0c;现在总结下来&#xff1a; 核心代码&#xff1a;VoucherOrderServiceImpl.java Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper,…

今天在学习黑马程序员的黑马点评实战项目中关于优惠券秒杀一人一单的时候碰到了一些问题,现在总结下来:

核心代码:VoucherOrderServiceImpl.java

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate ISeckillVoucherService seckillVoucherService;@Resourceprivate RedisIdWorker redisIdWorker;/*** 抢购秒杀优惠券** @param voucherId* @return*/public Result seckillVoucher(Long voucherId) {//1.查询优惠券信息SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断秒杀是否已开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未开始return Result.fail("秒杀尚未开始");}//3.判断秒杀是否已结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {// 已结束return Result.fail("秒杀已经结束");}//4.判断秒杀优惠券库存是否充足if (voucher.getStock() < 1) {// 库存不足return Result.fail("库存不足");}//获取用户IDLong userId = UserHolder.getUser().getId();//synchronized : 基于这个字符串对象加锁,同一用户的并发请求会串行执行。synchronized (userId.toString().intern()) {//直接调用类内部的createVoucherOrder方法,会导致事务注解@Transactional失效,因为Spring的事务是通过代理对象来管理的//return this.createVoucherOrder(voucherId);//获取当前的代理对象,使用代理对象调用第三方事务方法,防止事务失效IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();   //获取当前类的代理对象return proxy.createVoucherOrder(voucherId);}}/*** 通过数据库查询确保“一人一单”* @param voucherId* @return*/@Transactional   // 事务注解:保证订单创建和库存扣减的原子性,并且只有事务提交后,其他请求才能看到新订单和库存变化public Result createVoucherOrder(Long voucherId) {//5.一人一单Long userId = UserHolder.getUser().getId();//5.1查询数据库中是否已经存在该用户抢购该优惠券的订单int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();//5.2判断是否存在if (count > 0) {//用户已经购买过了,返回失败信息return Result.fail("用户已购买!");}//6.扣减库存boolean success = seckillVoucherService.update().setSql("stock = stock - 1")   //set stock = stock - 1.eq("voucher_id", voucherId).gt("stock", 0)   //where id = ? and stock > 0 数据库层面的乐观锁,避免超卖.update();if (!success) {//库存扣减失败return Result.fail("库存不足");}//7.创建订单(在订单表tb_voucher_order插入一条数据)VoucherOrder voucherOrder = new VoucherOrder();//7.1 订单idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//7.2 用户idvoucherOrder.setUserId(userId);//7.3 代金券idvoucherOrder.setVoucherId(voucherId);//插入到订单信息表save(voucherOrder);//8.返回订单id(生成唯一订单id并保存)return Result.ok(orderId);}
}

这段代码的主要实现了一个优惠券秒杀的功能,重点处理了并发情况下的线程安全和事务处理。

1. 整体流程

  1. 查询优惠券信息:检查秒杀活动的开始和结束时间,以及库存是否充足。

  2. 用户加锁:对用户ID加锁,防止同一用户并发请求。

  3. 创建订单:在事务中检查用户是否已购买、扣减库存并生成订单。

2. 关键代码解析

2.1 锁的实现:synchronized (userId.toString().intern())
  • 目的:确保同一用户的请求在同一时间只能有一个线程处理,防止重复下单。

  • 原理

    • userId.toString().intern() 将用户ID转换为字符串,并调用 intern() 方法,确保所有相同用户ID的字符串指向常量池中的同一个对象。

    • synchronized 基于这个字符串对象加锁,同一用户的并发请求会串行执行。

2.2 代理对象:IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy()
  • 目的:确保 @Transactional 注解生效。

  • 原因

    • Spring的事务管理基于AOP代理,直接调用内部方法(如 this.createVoucherOrder())会绕过代理,导致事务失效。

    • 通过 AopContext.currentProxy() 获取当前类的代理对象,再调用方法,事务才能正确应用。

  • 依赖:需在启动类添加 @EnableAspectJAutoProxy(exposeProxy = true) 暴露代理对象。

2.3 事务方法:createVoucherOrder
  • 检查用户订单:通过数据库查询确保“一人一单”。

  • 扣减库存

    • 使用条件更新:eq("voucher_id", voucherId).gt("stock", 0)

    • 数据库层面的乐观锁,避免超卖。

  • 创建订单:生成唯一订单ID并保存。

3. 并发安全设计

  1. 用户维度锁

    • 防止同一用户并发请求导致多次下单。

    • 锁的粒度较细(用户级别),不同用户的请求可并行处理。

  2. 数据库乐观锁

    • 扣减库存时检查 stock > 0,确保库存不为负。

  3. 事务隔离

    • @Transactional 保证订单创建和库存扣减的原子性。

    • 事务提交后,其他请求才能看到新订单和库存变化。

4. 代码总结

步骤操作并发安全措施
查询优惠券信息检查时间、库存
用户加锁synchronized 锁防止同一用户重复下单
创建订单(事务)检查用户订单、扣库存、插入订单数据库乐观锁、事务隔离

5. 关键问答

Q1: 为什么要用 userId.toString().intern() 作为锁?

确保同一用户ID的字符串在不同请求中是同一个对象,锁生效。

Q2: 为什么需要代理对象调用 createVoucherOrder

Spring事务通过动态代理实现,直接调用内部方法会绕过代理,事务失效

Q3: 如何防止超卖?

数据库更新时检查stock > 0,结合应用层锁和数据库乐观锁。

6. 事务的核心特性

在秒杀场景中,事务确保以下关键操作要么全部成功,要么全部回滚:

  1. 检查用户是否已下单SELECT)。

  2. 扣减库存UPDATE stock SET stock = stock - 1)。

  3. 创建订单INSERT INTO voucher_order)。

若缺少事务,可能出现:

  • 库存扣减成功,但订单创建失败 → 数据不一致(用户付钱没拿到券)。

  • 用户重复下单检查通过,但下单时库存不足 → 业务逻辑错误。

在上面的代码中,事务实现如下:

@Transactional
public Result createVoucherOrder(Long voucherId) {// 数据库操作:检查订单、扣库存、创建订单
}
  • 作用:Spring通过AOP代理为该方法添加事务管理。

  • 默认行为

    • 传播机制REQUIRED(如果当前有事务则加入,否则新建)。

    • 隔离级别ISOLATION_DEFAULT(依赖数据库默认,通常为可重复读)。

    • 回滚规则:遇到运行时异常(如RuntimeException)自动回滚。

6.1 为什么必须使用代理对象调用方法?
// 错误写法:直接调用this.createVoucherOrder(),事务失效!
IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
return proxy.createVoucherOrder(voucherId); // 通过代理对象调用
  • 根本原因Spring的事务基于动态代理(JDK或CGLib),直接调用内部方法会绕过代理,导致@Transactional失效。

  • 解决方案:通过AopContext.currentProxy()获取当前类的代理对象。

7. 事务与锁的协作流程

步骤操作事务与锁的作用
1. 用户请求进入执行seckillVoucher方法
2. 获取用户锁synchronized(userKey)防止同一用户并发操作
3.调用代理方法proxy.createVoucherOrder()触发事务管理(AOP代理)
4. 事务开始自动开启事务开启数据库事务
5. 检查用户订单SELECT COUNT(...)事务内读操作(可重复读保证一致性)
6. 扣减库存UPDATE stock ... WHERE stock>0事务内写操作(数据库乐观锁)
7. 创建订单INSERT INTO voucher_order事务内写操作
8. 事务提交自动提交所有操作原子生效
9. 释放用户锁退出synchronized允许其他线程处理该用户请求

7.1 关键协作点
  • 锁在事务外:先加锁,再开启事务。

    • 原因:若锁在事务内,事务提交前释放锁,其他线程可能读到未提交的数据(如库存未扣减)。

    • 当前代码正确性:锁包裹事务,确保事务提交后才释放锁。

  • 事务隔离级别

    • 默认隔离级别(可重复读)防止脏读、不可重复读,确保事务内多次读取数据一致。

7.2 事务在秒杀场景的小总结
操作无事务的风险有事务的保障
检查用户订单可能通过检查,但下单时冲突可重复读确保一致性
扣减库存超卖(库存扣减到负数)数据库乐观锁(stock > 0)阻止超卖
创建订单订单丢失或重复唯一键约束+原子提交保证数据完整

7.3 关键问答

Q1: 如果事务方法中抛出非RuntimeException,事务会回滚吗?

默认不会,需通过@Transactional(rollbackFor = Exception.class)配置。

Q2: 事务方法内调用其他事务方法,事务如何传递?

默认使用REQUIRED传播机制,合并到同一事务中。

8. 串行执行 VS 并发执行

8.1串行执行

定义:任务按顺序依次执行,前一个任务完成后,后一个任务才能开始。
特点

  • 简单、安全,不会出现资源冲突。

  • 效率低,无法充分利用多核CPU资源。
    场景:单线程程序、需要严格顺序的操作(如转账步骤)。

示例:

// 单线程串行执行任务
public static void main(String[] args) {task1();  // 任务1执行完毕task2();  // 任务2开始执行task3();  // 任务3开始执行
}

8.2 并发执行

定义:任务看似同时执行,实际通过时间片轮转或并行处理实现。
特点

  • 提高资源利用率,适合多核CPU。

  • 可能引发线程安全问题(如数据竞争)。
    场景:Web服务器处理多请求、批量数据处理。

示例:

// 多线程并发执行任务
public static void main(String[] args) {new Thread(() -> task1()).start();  // 任务1开始执行new Thread(() -> task2()).start();  // 任务2开始执行new Thread(() -> task3()).start();  // 任务3开始执行// 三个任务可能交替执行
}

8.3 代码中秒杀系统中的订单创建
synchronized (userId.toString().intern()) {IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);
}
  • 锁的作用
    强制同一用户的订单创建操作串行执行(即使有多个并发请求)。
    例如:用户A的3个请求会依次处理,但用户B的请求可以与用户A并发执行。

  • 并发与串行的平衡

    • 并发:不同用户的请求可以并行处理(提高吞吐量)。

    • 串行:同一用户的请求串行处理(避免重复下单)。

8.4 并发执行的风险和解决方案
8.4.1 超卖(库存扣减为负)

并发场景

  • 线程1和线程2同时查询库存为1。

  • 两者都认为可以扣减,最终库存变为-1。

解决方案:

// 使用数据库乐观锁
boolean success = seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).gt("stock", 0)  // 保证库存 > 0 时才扣减.update();

8.4.2 重复下单

并发场景

  • 同一用户的多个请求同时通过“一人一单”检查。

  • 最终插入多条订单。

解决方案:

// 对用户ID加锁
synchronized (userId.toString().intern()) {// 检查订单 + 创建订单(事务内)
}

9. Spring事务管理基于AOP的底层原理详解

9.1 AOP的核心概念

AOP(Aspect-Oriented Programming,面向切面编程)用于将横切关注点(如日志、事务、安全)与业务逻辑解耦

关键术语:

术语说明事务管理中的对应示例
切面(Aspect)横切多个类的模块化功能(如事务管理)@Transactional注解标记的方法
通知(Advice)切面在特定连接点执行的动作(如“在方法前后加事务”)事务的开启、提交、回滚操作
连接点(Join Point)程序执行的点(如方法调用)createVoucherOrder()方法的执行
切点(Pointcut)匹配连接点的表达式(定义哪些方法需要增强)通过@Transactional注解匹配需要事务的方法
目标对象(Target)被代理的原始对象VoucherOrderServiceImpl类实例
代理(Proxy)对目标对象增强后的对象(由AOP框架生成)Spring生成的IVoucherOrderService代理类

9.2 Spring事务管理的实现流程
9.2.1 事务管理本质是AOP的环绕通知

Spring通过AOP动态代理,在目标方法前后添加事务逻辑:

// 伪代码:事务的AOP环绕通知实现
public class TransactionAspect {@Around("@annotation(transactional)")public Object around(ProceedingJoinPoint pjp, Transactional transactional) {Connection conn = DataSourceUtils.getConnection();try {conn.setAutoCommit(false); // 1.开启事务Object result = pjp.proceed(); // 2.执行目标方法(如createVoucherOrder)conn.commit(); // 3.提交事务return result;} catch (Exception e) {conn.rollback(); // 4.回滚事务throw e;} finally {DataSourceUtils.releaseConnection(conn);}}
}

9.2.2 动态代理的两种实现方式

Spring根据目标类型选择代理方式:

代理类型条件示例
JDK动态代理目标类实现了接口IVoucherOrderService接口实现类
CGLib动态代理目标类未实现接口无接口的普通类

代码中为什么比用代理对象?

直接调用 this.createVoucherOrder() 会绕过代理,导致事务失效。

9.2.3 结合代码的完整AOP事务流程

seckillVoucher方法调用为例:

  1. Spring容器启动

    • 扫描到VoucherOrderServiceImpl类,发现它实现了IVoucherOrderService接口。

    • 检测到createVoucherOrder方法有@Transactional注解,为此类生成JDK动态代理对象。

  2. 方法调用过程

    // 用户调用IVoucherOrderService.seckillVoucher()
    IVoucherOrderService proxy = Spring容器中的代理对象;
    proxy.seckillVoucher(voucherId);// 代理对象内部执行:
    public Result seckillVoucher(Long voucherId) {// 1. 执行AOP前置逻辑(无事务)synchronized (userId.toString().intern()) {// 2. 调用原始对象的seckillVoucher方法IVoucherOrderService target = new VoucherOrderServiceImpl();target.seckillVoucher(voucherId); // 此时this指向原始对象// 3. 通过AopContext获取当前代理对象IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();// 4. 通过代理调用createVoucherOrder,触发事务return proxy.createVoucherOrder(voucherId);}
    }
  3. 事务方法触发

    • 代理对象拦截createVoucherOrder方法的调用。

    • 执行事务环绕通知:开启事务 → 执行方法 → 提交/回滚事务。

9.3 AOP与事务管理的关系
步骤AOP的作用事务管理的体现
代理对象生成通过JDK/CGLib创建目标类的代理@Transactional类生成代理
方法拦截在目标方法执行前后插入通知环绕通知中管理事务(开启、提交、回滚)
异常处理捕获方法抛出的异常根据异常类型决定回滚或提交
自调用问题解决通过暴露代理对象访问增强方法使用AopContext.currentProxy()

9.4 关键问答

Q1: 为什么事务注解要加在public方法上?

  • Spring的AOP默认基于代理,只能拦截public方法。

  • 若注解加在非public方法(如private),代理无法增强该方法,事务失效。

Q2: Spring AOP和AspectJ有什么区别?

  • Spring AOP基于动态代理,仅支持方法级别的切面;AspectJ是完整的AOP框架,支持字段、构造器级别的切面。

Q3: 如何强制Spring使用CGLib代理?

  • @EnableAspectJAutoProxy中设置proxyTargetClass=true

Q4: 事务方法中调用另一个事务方法,事务如何传递?

  • @Transactional(propagation=...)决定,默认REQUIRED(合并到同一事务)。

Q5: 事务失效的常见场景有哪些?

  • 自调用、非public方法、异常被捕获未抛出、数据库引擎不支持事务等。

http://www.dtcms.com/wzjs/1263.html

相关文章:

  • 没网站做cpa关键词的选取原则
  • 广州哪里有网站建设百度联盟点击广告赚钱
  • 长沙网站seo收费查询seo
  • 优秀网站制作定制sem是什么基团
  • 网站建设合并但与那个天津百度关键词推广公司
  • 做有搜索功能的网站免费视频外链生成推荐
  • 什么网站需要备案厦门人才网唯一官方网站
  • 株洲企业网站制作获客引流100种方法
  • 阿里妈妈网站怎么做月嫂免费政府培训中心
  • 重庆营销网站建设公司营销策划品牌策划
  • 硬件开发教程宁波seo优化公司排名
  • 呼和浩特市网站公司网络seo公司
  • 小白怎么做网站搬家教程淘宝关键词搜索工具
  • 广告设计公司怎么找业务seo搜索引擎优化简历
  • 做网站的版式会侵权吗谷歌站长平台
  • 沧县做网站获客软件排名前十名
  • 做网站类的书本信息seo外链专员
  • 全国培训加盟网站建设做网站企业
  • 鲅鱼圈网站制作seo快排公司哪家好
  • 上海网站设计联系方式快抖霸屏乐云seo
  • php团购网站开发厦门seo搜索排名
  • 凡科网站建设之后怎么删除win优化大师有用吗
  • 公司网站建设价格多少网络推广视频
  • 分类信息网站平台的推广如何分析百度指数
  • 东营做网站优化的公司阿里云域名注册流程
  • 怎么做时时彩彩票网站2023必考十大时政热点
  • 网页版传奇制作教程广州市网络seo外包
  • 广东网站开发软件企业的互联网推广
  • 英文网站设计哪家好网站注册步骤
  • 企业网站建设建设seo网站推广建站服务商