@Override @Async("asyncInvoiceExecutor") public void printInvoiceByType(List<InvoiceLog> invoiceLogList, Integer invoiceType, BillTaxControlInfo taxControlInfo) { for (InvoiceLog invoiceLog : invoiceLogList) { InvoiceLog invoiceLogResult = null; if (invoiceType == 1 || invoiceType == 2) { invoiceLogResult = commonInvoiceService.paperInvoiceAndPrint(invoiceLog, taxControlInfo); } else if (invoiceType == 3) { invoiceLogResult = commonInvoiceService.electronicRealTimeInvoice(invoiceLog, taxControlInfo); } logger.debug("======================= invoice print end...========================="+(invoiceLogResult == null)); if (invoiceLogResult != null) { invoiceLogService.updateByPrimaryKeySelective(invoiceLogResult); // updateInvoiceLog(invoiceLogResult); logger.debug("======================= update end...========================="); } } } @Transactional public int updateInvoiceLog(InvoiceLog invoiceLog){ return invoiceLogMapper.updateByPrimaryKeySelective(invoiceLog); }
如上代码所示,@Async首先得用在不同类中,所以下放到Service中的一个方法,这个方法有耗时操作(调用外部接口打印发票),打印完结果还要更新到数据库,为了避免大事物,将方法命名为printInvoiceByType(只读)。后面调用另一个service的方法更新记录结果,可是事物根本没提交,打印结果如下:
[18 14:28:25,176 DEBUG] [asyncInvoiceExecutor-1] rest.RestUtil - =============================调用返回结果:"{\"RetMsg\":\"4001-发票数据不合法 [A632,开票人长度不正确!]\",\"RetCode\":\"4001\"}"
[18 14:28:25,181 INFO ] [asyncInvoiceExecutor-1] impl.CommonInvoiceServiceImpl - 开具发票失败!===================>:4001-发票数据不合法 [A632,开票人长度不正确!]
[18 14:28:25,182 DEBUG] [asyncInvoiceExecutor-1] impl.InvoicingServiceImpl - ======================= invoice print end...=========================false
[18 14:28:25,183 DEBUG] [asyncInvoiceExecutor-1] InvoiceLogMapper.updateByPrimaryKeySelective - ==> Preparing: update bill_invoice_log SET invoice_status = ?, result_code = ?, result_msg = ? where invoice_id = ?
[18 14:28:25,183 DEBUG] [asyncInvoiceExecutor-1] InvoiceLogMapper.updateByPrimaryKeySelective - ==> Parameters: 9(Integer), 4001(String), 4001-发票数据不合法 [A632,开票人长度不正确!](String), 200011(Integer)
那个update end...也没打印,不知道怎么回事。事物是通过AOP切面配置的。
Thread dump 结果如下:
"asyncInvoiceExecutor-1@10943" prio=5 tid=0x29 nid=NA waiting
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Unsafe.java:-1)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
这到底怎么回事,阻塞了吗,也没报错。为什么产生这种现象。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
从您提供的代码片段和日志信息来看,问题可能出在几个方面:
异步方法执行与事务管理:@Async
注解使得printInvoiceByType
方法异步执行,这意味着它将在单独的线程中运行。而直接在该异步方法内调用带有@Transactional
注解的方法(如updateInvoiceLog
),可能会导致事务管理不生效。因为Spring的事务管理默认是基于代理的,当在一个类内部调用另一个有事务注解的方法时,由于没有通过代理对象,事务可能不会被正确传播。
日志打印与事务提交:您提到“事物根本没提交”,但实际上从日志中的SQL准备语句可以看出,更新操作已经到了数据库层面的准备阶段。这表明事务已经开始执行,但可能还未完成提交。事务的提交通常发生在方法正常结束且没有抛出未被捕获异常的情况下。如果在日志打印之前程序挂起或阻塞了,那么"update end..."的日志自然不会出现。
线程堆栈分析:根据提供的Thread dump信息,线程asyncInvoiceExecutor-1
的状态为WAITING
,说明它正在等待某个条件满足(比如任务队列中有新的任务)。这可能是正常的异步线程池行为,即在完成当前任务后等待新任务的到来,而不是出现了死锁或者严重的阻塞情况。但是,如果这个状态持续并且没有其他活动迹象,可能意味着上游的任务调度出了问题,或者是线程池配置不当(例如队列已满)。
事务配置检查:确保您的事务管理配置正确地包含了异步方法的处理。对于异步方法上的事务管理,需要特别注意配置以确保事务能够跨线程正确传播。
解决建议: - 确认@Transactional
是否正确配置,并考虑是否需要在异步方法上也使用@Transactional
,虽然这通常不是推荐做法,因为异步方法的事务管理更为复杂。 - 检查是否有任何未捕获的异常阻止了事务的正常提交。 - 查看线程池配置,确认其大小、队列设置是否合理,避免因资源耗尽导致任务无法继续执行。 - 考虑使用更详细的日志记录,包括事务开始、提交、回滚等关键点,以便于追踪事务的生命周期。 - 如果可能,审查AOP切面配置,确保事务增强逻辑对异步方法同样有效。