实现Spring中的任务调度及异步执行

    首先要端正一下本人的态度,开发了很长时间的Java Web项目,寄托于Spring MVC的架构,多数时间都是在处理业务逻辑问题,因此我我的单纯地认为Web开发,多线程的应用场景应该很少,能不用尽可能不用(固然,有这样的想法,那也是我我的多线程运用很少,理解不是很深入,并且多线程并发操做须要面对和处理的问题不少,像共享资源上锁等)。但如今须要用到了,才明白多线程的应用场景是多么的重要。因此这几天开始在多线程方面进行了研究,下面总结我这几天的理解和认知。html

    先阐述下,我在Web项目开发中为何会用到多线程?项目背景是这样的:门户网站中添加了页面静态化的功能,那对于静态页面内容的更新,采用什么更新策略呢?我用的是最简单的方法,启用定时任务,每隔一段指定的时间清空静态页面文件所在的目录(关于这点,若是有更好作法或建议的,请指点一哈),在项目开始运行时,就启用定时任务开始工做。显然,启用定时任务,能够简化认为开始一个新的线程,来专门执行删除静态页面文件的操做。java

    为了实现上述的功能,不须要咱们从最原始的多线程编程方式入手,经过网上的搜索,大体有三种解决方案。以下:spring

  • 使用Quartz开源任务调度框架。编程

  • 使用JDK Timer类。多线程

  • 使用Spring“原生态”的Spring Task。架构

    针对前两种方法,Spring都给予了完美的封装和整合。那么关于Quartz和JDK Timer是什么,作什么,怎么用,能够本身搜索下,这里不做重点描述。但须要强调的是,这么多的方法,哪一个是适用的。并发

    1、多种任务调度方案的对比框架

  • Quartz:复杂,重量级,功能强大;能够实现定时、定点的任务调度;提供了调度运行环境的持久化机制;提供组件式的侦听器、各类插件、线程池等功能。异步

  • JDK Timer:能够完成简单的定时任务调度;只适合执行时间很是短的任务调度;因为Timer中全部的任务在单一的背景线程中运行,常常会出现时间漂移、任务挤压等问题。工具

  • Spring任务调度:能够看做是轻量级的“Quartz”,能够实现定时、定点的任务调度功能;是Spring自带的功能,使用简单方便。

    综合上述内容,因为要解决的问题很简单,就选用了Spring任务调度。固然,若是须要更为复杂的任务调度功能时,仍是要选用Quartz的。至于JDK Timer,局限太多,不做考虑了。选定了解决方案后,谈一下我在学习和了解过程的一些感悟。

    二、理解任务、异步、多线程

    我在调研Quartz、JDK Timer和Spring任务调度的过程当中,见到最多的关键词是任务、调度、异步、线程等。可能有人会疑惑,本文开头我强调的是多线程,可说到这儿用到的倒是任务调度,这两个概念有何关联?我谈下个人理解,不必定对,欢迎指正。

    我在看相关资料时,包括Spring文档中,相提并论是任务调度和异步执行。我认为:

  • 任务的重点在于调度。

  • 异步(操做)的重点在于并发。

  • 一个异步操做也能够看成一个任务,但可能并不涉及调度。

    上述两点的实现依靠多线程的技术。而其中Quartz以及Spring的封装、优化中都提到了线程池技术,我认为线程池是多线程编程,或者准确地说并发操做的一种优化措施。

    3、认知TaskExceutor和TaskScheduler

    在学习Spring文档中任务调度的相关部分时,接触到了TaskExecutor和TaskScheduler。这里我只简单描述一下本身的理解,由于本身感受理解的不是很透彻,因此这点还望高人看到后能给予补充纠正。

    JDK5.0新增了一个并发工具包java.util.concurrent,其中执行器Executor是其中一个重要的类。Executor的存在是为了方便处理并发操做,它对Runnable实例的执行进行了抽象,使实现者能够提供更为丰富的实现。JDK5.0提供的实现者大多拥有线程池的内在支持,Spring为Executor处理引入了新的抽象层,继而诞生了TaskEexcutor和TaskScheduler。

    不过值得注意的是,TaskExecutor的实现也提供了不少线程池的支持,可见利用线程池来处理数量巨大的短小并发任务是有很大好处的,具体能够了解下Java线程池的意义。但TaskExecutor并不全是使用线程池的,具体使用场景能够查看Spring文档中关于TaskExecutor的实现。

    我所认为的是,TaskExecutor的做用是绑定Runnable实例,能够决定是否实现异步操做,相似咱们使用过的new Thread(Runnable run)。而TaskScheduler更多的则是为了绑定异步操做时,同时设定调度规则。但谈到这里,我抛出一个疑惑,非异步操做的任务是否存在调度的意义?我的感受关于TaskScheduler的实现应用较多仍是ThreadPoolTaskScheduler,主要是对异步任务的调度处理。下面来具体谈一下使用Spring对任务调度的实现吧。

    4、Spring任务调度功能的理解

    有种较好的理解,应用程序执行的与用户行为无关的操做成为后台任务。而Spring对后台任务分为了两种,并提供了封装与实现,以下:

  • 调度任务

  • 异步方法

    Spring一样提供了xml配置和注解两种方式来实现后台任务的功能。

    http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/scheduling.html中描述的很详细,这里我再也不作过多说明。重点强调我对一些关键点的理解。

  • 不管是注解仍是xml配置,在用到了task命名空间,须要在Spring的配置文件中声明task的命名空间。

  

<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>


  • 如上代码所示,使用注解方式,须要在Spring配置文件中添加<task:annotation-driven />。<task:executor .../>

    会建立一个ThreadPoolTaskExecutor实例,负责处理标注了@Async注解的异步任务。<task:scheduler .../>会建立一个ThreadPoolTaskScheduler,负责管理调度标注了@Scheduled的方法。可见这样的配置都采用了线程池的技术。若是没有设置pool-size属性,线程池中默认只保持一个线程。

   @Scheduled注解能够对指定方法进行调度,配合fixedDelay、fixedRate和cron属性,能够制定详细的调度规则。

  @Async注解能够指定方法,在被调用时,以异步的方式执行。

相关文章
相关标签/搜索