添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1、以下代码存在并发问题,原因是@Transactional开启事务后,执行完createOrder()方法后已经释放锁了,但是事务还没提交,此时另外一个线程获取到锁开始执行createOrder方法导致的

public class ChaoMaiConcurrencyService {
    public static final int purchaseProductId = 1;
    public static final int purchaseProductNum =1;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Transactional(rollbackFor = Exception.class)
    public synchronized Integer createOrder() throws Exception {
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product == null) {
            throw new Exception("购买商品:"+purchaseProductId+"不存在");
        Integer currentCount = product.getCount();
        //校验库存
        if (purchaseProductNum > currentCount) {
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
        //更新库存
        productMapper.updateProductCount(purchaseProductNum,product.getId());
        //新增订单
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);
        order.setReceiverName("xxx");
        orderMapper.insertSelective(order);
        //新增订单明细
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItemMapper.insertSelective(orderItem);
        return order.getId();
测试类代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ChaoMaiConcurrencyTest {
    @Autowired
    private ChaoMaiConcurrencyService chaoMaiConcurrencyService;
    @Test
    public void testChaoMai() throws InterruptedException {
        CountDownLatch cdl = new CountDownLatch(5);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        ExecutorService es = Executors.newFixedThreadPool(5);
        for (int i=0;i<5;i++) {
            es.execute(()->{
               try {
                   cyclicBarrier.await();
                   Integer orderId = chaoMaiConcurrencyService.createOrder();
                   System.out.println("订单id:"+orderId);
               } catch (Exception e) {
                   e.printStackTrace();
               } finally {
                   cdl.countDown();
            });
2、解决方案:不使用@Transactional注解,注入PlatformTransactionManagerTransactionDefinition来手动提交事务
代码如下:
public class ChaoMaiConcurrencyService {
    public static final int purchaseProductId = 1;
    public static final int purchaseProductNum =1;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    public synchronized Integer createOrder() throws Exception {
        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product == null) {
            //手动回滚
            platformTransactionManager.rollback(transaction);
            throw new Exception("购买商品:"+purchaseProductId+"不存在");
        Integer currentCount = product.getCount();
        //校验库存
        if (purchaseProductNum > currentCount) {
            //手动回滚
            platformTransactionManager.rollback(transaction);
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
        //更新库存
        productMapper.updateProductCount(purchaseProductNum,product.getId());
        //新增订单
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);
        order.setReceiverName("xxx");
        orderMapper.insertSelective(order);
        //新增订单明细
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItemMapper.insertSelective(orderItem);
        //手动提交
        platformTransactionManager.commit(transaction);
        return order.getId();
                                    管理事务:
    开启事务: setAutoCommit( boolean autoCommit) : 调用该方法设置参数为false,即开启事务
    提交事务: commit()
    回滚事务: rollback()
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
Savepoint savepoint = null;
try {
                                    最近项目当中有个场景问题,需要在数据更新完成之后,发送mq消息通知周边系统进行操作。但是周边系统需要用到当前事务更新完成的数据。经常出现mq消息处理过程中拿不到更新之后的数据。经过排查日志,查看sql执行情况,代码逻辑。还跟组内成员反复查看代码执行逻辑。就是没发现问题点在哪里!
然后只能跟踪debug代码执行过程,终于发现在事务执行过程中,没看到commit的时候,mq就把消息发出去了。
然后查看源码,发现在事务提交后,提供了afterCommit的方法,需要我们自己来实现。
解决方案:
1、把mq发消息放
                                    在Java中,事务Transaction)是指作为单个逻辑工作单元执行的一系列操作。当涉及到数据库操作时,事务确保了数据的完整性和一致性,即使在出现故障的情况下也能保证数据的安全。事务的处理通常在数据库管理系统(DBMS)层面进行,但在Java环境中,事务的管理也可以通过编程框架如Spring或Java Transaction API (JTA)来控制。以上即为SpringBoot的事务开启、配置与使用方式!更多资源也可关注微信公众号获取!
                                    一个程序中不可能没有事务,而 Spring 中,事务的实现方式分为两种:编程式事务和声明式事务,又因为编程式事务实现相对麻烦,而声明式事务实现极其简单,所以在日常项目中,我们都会使用声明式事务 @Transactional 来实现事务。
                                    当我们开启一个事务时,通常需要用commit或rollback结束事务。但是有些操作会自动提交事务,比如DDL语句。这种自动提交事务的操作就是隐式事务。如果存在事务A,那么将事务A挂起,并开启一个新事务。那么当标志有REQUIRES_NEW的内部事务发生异常回滚时,外部事务也会一起回滚。当第3步发生异常时,1、2都不会回滚。3、一个事务没有结束,又开启了新的事务,前一个事务就会被自动提交。2、创建、更新或者删除用户的操作也会导致事务隐式提交。拿我遇到的场景举例:在第3步的方法上添加事务注解。
                                    文章目录1 背景2 问题分析2.1 mysql隔离级别2.2 问题原因分析3 解决问题方案3.1 方式一3.2 方式二
业务处理过程,发现了以下问题,代码一是原代码能正常执行,代码二是经过迭代一次非正常执行代码
代码一:以下代码开启线程后,代码正常执行
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, Tim...
                                    我在使用Spring(3.0.5),Hibernate(3.6.0)和Wicket(1.4.14)开发应用程序时遇到了奇怪的问题.问题是:我无法将任何对象保存或修改为数据库.通过’不能’,我的意思是对象的所有更改或对EntityManager.persist(foo)的调用都被简单地,默默地忽略.选择工作.示例案例很简单 – 在一些wicket页面上我尝试将对象保存到数据库中,如下所示public...
                                    点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析...
                                    除非特殊配置(比如使用 AspectJ 静态织入实现 AOP),否则只有定义在 public 方法上的 @Transactional 才能生效。因为Spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。
必须通过代理过的类从外部调用目标方法才能生效。Spring 通过 AOP 技术对方法进行增强,要调用增强过的方法必然是调用代理后的对象。因为this 指针代表对象自己,Spring 不可能注入 this,所..
                                    这个手动开启事务,需要每个方法都需要实现,这个也是比较繁琐,这种方式可以抽象出一个公共类,统一来实现事务的处理。方法一和方法二的效率都比较低,另一种方式可以把,可以把需要 把需要并发控制的业务,单独抽离出来,进行事务控制操作。由上图可以看出,当线程1 释放了锁,还提交事务之前,线程2 已经获取锁并提前提交事务,从而导致并发问题。不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。注解的方法 添加 AOP切面来时先事务管理的。最大范围也就是方法级别的。