前言介绍
开篇咱们先来聊聊线程池这个概念,或者说为什么要使用线程池;简言之,充分利用cpu资源,提高程序执行时间,但是相反,线程池异常提示、主线程和子线程事务问题也是显而易见的。那么@Async这个注解又是什么做用呢?其实就是标识方法为异步任务的一个注解,默认会自己维护一个线程池(存在弊端),利用子线程去执行任务;那么如果把这两者结合的话,线程池+Async又会有什么效果呢!
循序渐进
提到线程池,可以采用Executors提供四种线程池下,使用某些特性的场景下,还是不错的(简便省事),当然此篇文章就不进行描述,我们一块来看看自定义线程池的配置,具体参数自行查阅,需要注意“核心线程数”、“最大线程数”、“线程队列数值”配置:
接下来便要将Async和线程池一块使用了,使用之前请先到springboot启动类上加上@EnableAsync注解,具体使用方式如下:
此时便是开始在主线程里面通过多线程来使用异步任务,不过此时也需要分情况考虑,这里便提前和大家一块说了。
1:异步任务是否和主线程有关联,简言之,就是两者之间是否相互不影响?
2:如果不影响的话,主线程和异步任务的子线程直接使用便可以;
3:如果影响的话,主线程是要等待子线程执行完的结果的,此时便可以考虑加锁、或者使用一些提供好的并发类,比如CountDownLatch、CyclicBarrier,两者的区别请自行查阅,结合项目中的需求,我这里使用的前者CountDownLatch 。
如果没有其它业务需求的话,那么此时到这里便可以正常使用了,但是如果碰到子线程中有一些“自定义的提示信息”,或者是“自定义的异常信息”,如果单纯的考虑在主线程中通过Try…catch,或者SpringMVC中全局异常拦截**@RestControllerAdvice**能够进行处理,那就大错特错了,根本不可能实现的;那么此时我们便需要这样使用,才可以让主线程中捕获信息,然后返回给接口调用方-前端。
但是你以为这样就结束了么,相对于上面的操作,我们可以将子线程是否需要返回提示信息分为以下两种情况:
1:需要返回,子线程抛异常后,通过主线程返回提示信息,那么采用上面图片的处理方式
2:不需要返回,子线程抛异常后,要在某些地方进行日志记录等等,此时可以换一种方式定义线程池,具体方式如下,本人亲自测试过。
难道这篇博客的介绍就此结束了,休想,强行灌输一波,主线程和多线程中的子线程事务问题,具体情况如下:
1:主线程异常、子线程未执行,此情况不会有任何影响
2:主线程先执行,子线程异常,此时会有影响;
2.1:主线程中加入@Transactional(rollbackFor = Exception.class)注解,子线程不加事务注解,此时如果都正常的话,那么主线程和子线程在同一事务里面,一块提交;反之子线程如果不正常的话,那么主线程和子线程都不会进行提交;
2.2:主线程加入事务注解,子线程中也加入事务注解@Transactional(propagation = Propagation.REQUIRES_NEW),如果都正常的话,子线程提交后主线程才会提交;反之主线程和子线程都不会进行提交。
问题思考
其实在上面设计过程中,偏于实践操作的部分多一些,当然如果会一些理论知识会更好理解的;在项目中使用多线程技术后,发现多线程其实也并没有那么难理解,更多的都是在定义方面,这点也很好解决,反复理解和思考就行了;
个人认为,利用一些锁的方式来解决主线程和子线程协同的问题会更帅一些 !
下篇博客,Rabbitmq在项目中的操作实战,邀您来看。