JavaEE开发之Spring中的多线程编程以及任务定时器详解
上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于《JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换》。本篇博客我们就聊一下Spring中的并发编程,看一下Spring中的多线程编程和任务的定时执行。下方我们就来聊一下这两方面的内容。
一、Spring中的多线程
本部分就来看一下Spring框架封装下的多线程编程。因为毕竟是被Spring封装过的异步并发编程,所以用起来还是蛮简单的。主要还是ThreadPoolTaskExecutor的使用。下方会给出Spring框架中多线程编程的小示例。
1、创建异步执行任务的Service
下方的AsyncTaskService类就是我们创建的可支持异步任务执行的Service。主要使用了@Async注解来声明方法,使其支持异步任务的执行。在AsyncTaskService方法中,将当前线程的ID进行了打印,以便于我们进行观察。具体如下所示:

2、配置类中的异步设置
我们需要在Spring的配置类中进行异步的相关配置,然后我们使用@Async注解的方法才支持异步执行。下方就是相关的异步配置,首先使用@EnableAsync注解开启异步任务支持,然后实现AsyncConfigurer接口即可。下方TaskExecutorConfig配置类中的两个方法就是AsyncConfigurer接口的方法。
在该接口中,getAsyncExecutor()方法负责提供ThreadPoolTaskExecutor类的对象,在该方法中,我们实例化了ThreadPoolTaskExecutor类对象,然后对其进行了相应的配置。corePoolSize的值说明可开启核心线程数,稍后会进行演示。而maxPoolSize的值是可开启的最大线程数,queueCapacity的属性表示每个线程中可容纳的任务数。
而下方的getAsyncUncaughtExceptionHandler()方法是负责提供异常错误的句柄的。我们可以创建一个继承自ErrorHandler类的错误处理句柄类,在这个类中重写handleUncaughtException()方法,之后在该方法中返回该错误处理句柄的对象即可。当有异常时,会执行我们创建的这个错误句柄中相应的handleUncaughtException()方法。下方我们没有给出错误处理的句柄,直接就返回null即可。

我们可以看一下ThreadPoolTaskExecutor类中的属性,下方是这些属性的默认值了。

3.创建测试的Main方法
下方就是我们创建用来测试的Main方法,其中从Spring容器中获取AsyncTaskService的对象,然后在For循环中调用该对象的异步方法即可。具体代码如下所示:

4.运行结果
我们分别给ThreadPoolTaskExecutor对象的corePoolSize和queueCapacity属性设置相应的值,然后看起运行结果。
(1) corePoolSize = 10 && queueCapacity = 10(并行队列的异步执行)
下方是我们将开启线程数和线程队列容量都设置为10的运行结果。从下方我们可以看出,开启了10个线程,然后进行的异步执行。类似于iOS开发中GCD的并行队列的异步执行方式。

(2) corePoolSize = 1 && queueCapacity = 10(串行队列的异步执行)
接着我们将开启线程的最大值设置为1,然后将每个线程队列的容量设置为10。下方是其运行输出结果,我们可以看出只开启了一个新的线程来顺序执行这for循环中的10次任务都会在这个线程队列中排队执行。这也就是串行队列的异步执行了。

二、任务定时器
接下来我们就来看一下Spring框架中是如何使用@Schedule注解来实现任务定时执行的。@Scheduled注解中,有一些参数,我们可以为这些参数提供不同值来指定不同类型的Schedule。在@Scheduled任务定时器中,我们常用的属性有fixedRate、fixedDelay, cron这三个属性。下方我们将分别讨论着三个属性的具体用法,特别是cron属性,功能是比较强大的。废话少说,进入本部分的主题。
1、开启Schedule支持
首先我们得在Java配置类中开启Schedule的支持,也就是在配置类中添加上@EnableScheduling注解。具体如下所示。配置完后,我们就可以在我们的Service类中使用@Schedule注解来创建定时任务了。

2、创建定时执行的任务测试方法
接下来,我们就来创建Service类中的定时任务执行的测试方法。dateFormat属性负责日期的格式化,sleepTimes数组中的数字则代表每次执行任务所休眠的时间,用来模拟每次任务执行所需要的时间。使用index来标记当前执行的任务次数。
每调用一次testCase()方法,任务就执行一次。testCase()方法中的代码比较简单,在此就不做过多赘述了。

3、fixRate = 3000
fixRate = 3000表示两个相邻任务的开始执行时间的间隔必须大于等于3000毫秒。下方代码片段,是将fixeRate()方法使用@Scheduled声明为定时任务。在fixedRate()方法中调用了this.testCase()方法。在@Scheduled注解中,我们为fixedRate属性指定了一个值为3000ms, 也就是3秒的时间。下方我们会根据运行结果,来看一下fixedRate = 3000的具体作用。

下方截图,就是上述代码运行的测试结果,也就是fixedRate = 3000时的运行结果。然后我们也根据这个结果画出啊了一个任务执行的时间轴。 第一个任务执行开始到结束使用了1秒钟的时间,因为我们设定任务执行的固定频率是3秒,所以下次任务要经过两秒后才能执行。也就是说fixedRate = 3000,意味着从上一个任务执行开始,到下一个任务开始执行的间隔必须大于等于3秒,如果上一个任务的执行时间大于等于3秒的话,那么该任务执行完毕后,就紧接着执行下个任务。

4、fixedDelay = 3000
下方代码段是将fixedDelay属性设置成3000ms,表示在上次任务执行完成之后间隔3秒后在执行下一次任务。

下方就是上述代码所输出的结果,从下方结果中我们不难看出,上个任务结束的时间与下个任务开始的间隔为3秒。具体结果如下所示:

5、cron="0/3 * * * * ?"
cron属性后边紧跟着的是一个表达式,该表达式可表示特定的时间以及某些时间段,当系统时间到达我们设定的时间或者时间段后就会执行我们所指定的任务。在下方代码片段中,我们将cron的值设置为"0/3 * * * * ?"。该表达式的第一个参数就代表着秒,后边的参数表示任意。0/3表示从秒开始每3秒执行一次。

下方就是上述代码的运行结果,从下方结果中我们可以看出,从上一个任务的结束,到下一个任务的开始并不是中间隔着3秒的时间。而是本次任务结束后,如果下次任务开始执行的时间是3秒的倍数,那么下个任务就开始执行。如果不是,就继续等待。所以,我们不难看出下方任务开始的时间都是3的倍数。

6、cron的参数表达式
上一小节只是给出了cron参数的一种形式,接下来我们将详细的看一下cron的参数表达式的构建规则。下方是cron表达式每个位置所表示的时间值,以及取值范围。

秒:表达式的第一位是秒,允许值是0-59,"1,3,5"表示第1、3、5秒执行一次任务,“12-15”相当于12,13,14,15,表示这个范围内的秒数每秒执行一次。“*”就是同配符了,表示任意秒数。“3/5”表示从第三秒开始,每5秒执行一次。(, - * /)
分钟:分钟可支持的表达式形式与秒数一致,可以是“0-59”,“23,45,59”,“3/8”,“*”等格式。(, - * /)
小时:与分钟和秒的差不多,其允许的范围是0-23,可以使用“, - * /”。(, - * /)
日期:日期的范围是1-31,可以表示为“1-5”,“1,4,5”, “*”。“?”表示无意义的值,类似于“*”号。“2/3”,“L”也就是Last的缩写表示该月的最后一天。“W”就是Work的缩写,用法为“18W”表示离18号最近的工作日,比如18号是周日,那么里18号最近的工作日就是下周一了,那也就是19号。如果18号是周六,那么离18号最近的工作日就是本周的周五,也就是17号。如果18号就是周一~周五的某一天,因为18号当天就是工作日,所以“18W”就表示18号就是18号。(? , - * / L W C)
月份:月份的范围是在1~12个月,其中可以使用(, - * /)。
星期:星期的范围是1-7,1表示周日,7表示周六。可以使用(? , - * / L C #)。#号只能在星期中使用,2#3则表示当月第三个个星期的星期一。
年(可选): 范围为1970-2099,可以使用(, - * /)。
看完上面,我们可以给出一个综合的实例,比如“2,6-8 3/5 * LW 3 ? *”则表示离每年的3月份最后一天最近的工作日中每小时的3-5分钟的第2秒以及6~8秒执行一次任务。其实这种表达信息的方式就类似于正则表达式,也就是火星文。cron的用法还是比较灵活的,而且是比较强大的。
本篇博客就先到这儿吧,下篇博客我们会继续聊Spring的相关内容。
github源码分享地址:https://github.com/lizelu/SpringDemo
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #4e9072 }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #4e9072 }
JavaEE开发之Spring中的多线程编程以及任务定时器详解的更多相关文章
- JavaEE开发之Spring中的条件注解组合注解与元注解
上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条 ...
- JavaEE开发之Spring中的条件注解、组合注解与元注解
上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条 ...
- JavaEE开发之Spring中Bean的作用域、Init和Destroy方法以及Spring-EL表达式
上篇博客我们聊了<JavaEE开发之Spring中的依赖注入以及AOP>,本篇博客我们就来聊一下Spring框架中的Bean的作用域以及Bean的Init和Destroy方法,然后在聊一下 ...
- JavaEE开发之Spring中的依赖注入与AOP编程
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- JavaEE开发之Spring中的依赖注入与AOP
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换
本篇博客我们就来聊一下Spring框架中的观察者模式的应用,即事件的发送与监听机制.之前我们已经剖析过观察者模式的具体实现,以及使用Swift3.0自定义过通知机制.所以本篇博客对于事件发送与监听的底 ...
- JavaEE开发之SpringMVC中的自定义拦截器及异常处理
上篇博客我们聊了<JavaEE开发之SpringMVC中的路由配置及参数传递详解>,本篇博客我们就聊一下自定义拦截器的实现.以及使用ModelAndView对象将Controller的值加 ...
- JavaEE开发之SpringMVC中的静态资源映射及服务器推送技术
在上篇博客中,我们聊了<JavaEE开发之SpringMVC中的自定义拦截器及异常处理>.本篇博客我们继续的来聊SpringMVC的东西,下方我们将会聊到js.css这些静态文件的加载配置 ...
- JavaEE开发之SpringMVC中的自定义消息转换器与文件上传
上篇博客我们详细的聊了<JavaEE开发之SpringMVC中的静态资源映射及服务器推送技术>,本篇博客依然是JavaEE开发中的内容,我们就来聊一下SpringMVC中的自定义消息转发器 ...
随机推荐
- Xcode插件包Alcatraz
安装命令 curl -fsSL https://raw.github.com/alcatraz/Alcatraz/master/Scripts/install.sh | sh 终于可以了 这个其实 ...
- 基于微博数据用 Python 打造一颗“心”
一年一度的虐狗节刚过去不久,朋友圈各种晒,晒自拍,晒娃,晒美食,秀恩爱的.程序员在晒什么,程序员在加班.但是礼物还是少不了的,送什么好?作为程序员,我准备了一份特别的礼物,用以往发的微博数据打造一颗“ ...
- 纪中集训 Day 5
不知不觉已经day 5了啊 今天早上醒来,觉得要AK的节奏,结果就立flag了 - - 30分QAQ 其实第一题应该得想得到的,还有T2也能够解决的(话说后来看别人的代码写的好赞啊QAQ) 然后下午就 ...
- 每天一个Linux命令(22)--find命令详解
find 一些常用参数的一些常用实例和一些具体用法和注意事项. 1.使用 name 选项: 文件名选项是 find 命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用. 可以使用某种文件名模 ...
- 阿里云开发之OSS数据迁移
最近由于项目需求,需要将一个aliyun账号下的oss数据导入到两一个aliyun账号下的oss,经过一番坎坷,最终搞定. 1.查看oss数据迁移官方文档,我是在本地windows电脑上进行操作的,先 ...
- Java调度框架Quartz简单示例
Quartz的大名如雷贯耳,这里就不赘述,而且本文也不作为深入探讨,只是看完Quartz的官方文档后,下个简单示例,至少证明曾经花了点时间学习过,以备不时之需. Quartz使用了SLF4J,所以至少 ...
- 1574: [Usaco2009 Jan]地震损坏Damage
1574: [Usaco2009 Jan]地震损坏Damage Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 425 Solved: 232[Subm ...
- 使用awk截取某时间段的日志
想要取出文件里面时间是9点到12点的数据,文件内容如下: 2012-09-05 01:48:47,150 WARN [WorkManager(3)-72] [service.PhoneRangeMa ...
- 机器学习基石 5 Training versus Testing
机器学习基石 5 Training versus Testing Recap and Preview 回顾一下机器学习的流程图: 机器学习可以理解为寻找到 \(g\),使得 \(g \approx f ...
- .Net编译运行原理
.Net Framework: 它是框架库和运行时的集合 ( FCL, Framework Class Library ) ( CLR, Common Language Runtime ) 不严格说它 ...