springboot 线程池的使用和扩展
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
我们常用 ThreadPoolExecutor 提供的线程池服务,springboot 框架提供了 @Async 注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行,今天我们就来实战体验这个线程池服务,并根据实际需要做定制化的扩展;
实战环境
windowns10;
jdk1.8;
springboot 1.5.9.RELEASE;
开发工具:IntelliJ IDEA;
实战源码
本次实战的源码可以在我的 GitHub 下载,地址:git@github.com:zq2599/blog_demos.git ,项目主页:https://github.com/zq2599/blog_demos
这里面有多个工程,本次用到的工程为 threadpooldemoserver,如下图红框所示:
实战步骤梳理
本次实战的步骤如下:
创建 springboot 工程;
创建 Service 层的接口和实现;
创建 controller,开发一个 http 服务接口,里面会调用 service 层的服务;
创建线程池的配置;
将 Service 层的服务异步化,这样每次调用都会都被提交到线程池异步执行;
扩展 ThreadPoolTaskExecutor,在提交任务到线程池的时候可以观察到当前线程池的情况;
创建 springboot 工程
用 IntelliJ IDEA 创建一个 springboot 的 web 工程 threadpooldemoserver,pom.xml 内容如下:
创建 Service 层的接口和实现
创建一个 service 层的接口 AsyncService,如下:
对应的 AsyncServiceImpl,实现如下:
这个方法做的事情很简单:sleep 了一秒钟;
创建 controller
创建一个 controller 为 Hello,里面定义一个 http 接口,做的事情是调用 Service 层的服务,如下:
至此,我们已经做好了一个 http 请求的服务,里面做的事情其实是同步的,接下来我们就开始配置 springboot 的线程池服务,将 service 层做的事情都提交到线程池中去处理;
springboot 的线程池配置
创建一个配置类 ExecutorConfig,用来定义如何创建一个 ThreadPoolTaskExecutor,要使用 @Configuration 和 @EnableAsync 这两个注解,表示这是个配置类,并且是线程池的配置类,如下所示:
注意,上面的方法名称为 asyncServiceExecutor,稍后马上用到;
将 Service 层的服务异步化
打开 AsyncServiceImpl.java,在 executeAsync 方法上增加注解 @Async("asyncServiceExecutor"),asyncServiceExecutor 是前面 ExecutorConfig.java 中的方法名,表明 executeAsync 方法进入的线程池是 asyncServiceExecutor 方法创建的,如下:
验证效果
将这个 springboot 运行起来(pom.xml 所在文件夹下执行 mvn spring-boot:run);
在浏览器输入:http://localhost:8080;
在浏览器用 F5 按钮快速多刷新几次;
在 springboot 的控制台看见日志如下:
如上日志所示,我们可以看到 controller 的执行线程是"nio-8080-exec-8",这是 tomcat 的执行线程,而 service 层的日志显示线程名为“async-service-1”,显然已经在我们配置的线程池中执行了,并且每次请求中,controller 的起始和结束日志都是连续打印的,表明每次请求都快速响应了,而耗时的操作都留给线程池中的线程去异步执行;
扩展 ThreadPoolTaskExecutor
虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个 ThreadPoolTaskExecutor 的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来,代码如下:
如上所示,showThreadPoolInfo 方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后 Override 了父类的 execute、submit 等方法,在里面调用 showThreadPoolInfo 方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;
修改 ExecutorConfig.java 的 asyncServiceExecutor 方法,将 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改为 ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(),如下所示:
再次启动该工程,再浏览器反复刷新 http://localhost:8080,看到的日志如下:
注意这一行日志:2. do submit,taskCount [101], completedTaskCount [87], activeCount [5], queueSize [9]
这说明提交任务到线程池的时候,调用的是 submit(Callable<T> task)这个方法,当前已经提交了 101 个任务,完成了 87 个,当前有 5 个线程在处理任务,还剩 9 个任务在队列中等待,线程池的基本情况一路了然;
至此,springboot 线程池服务的实战就完成了,希望能帮您在工程中快速实现异步服务;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/3582294585082d2997360f60e】。文章转载请联系作者。
评论