您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Spring任務執行和調度的示例分析”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Spring任務執行和調度的示例分析”這篇文章吧。
一、概述
Spring框架分別使用TaskExecutor和TaskScheduler接口提供異步執行和任務調度的抽象。Spring還提供了這些接口的實現,這些接口支持線程池或將其委托給應用服務器環境中的CommonJ。
二、TaskExecutor
Spring 2.0 開始引入的新的抽像。Executors 是線程池的Java 5名稱。之所以稱作是“執行器”是因為不能保證底層實現實際上是一個池;執行程序可以是單線程的,甚至是同步的。Spring的TaskExecutor接口與java.util.concurrent是等價的。
2.1 TaskExecutor類型
SimpleAsyncTaskExecutor
線程不會重用,每次調用開啟一個新的線程。支持并發,超過最大并發調用數時,會阻塞,直到釋放一個槽為止。
SyncTaskExecutor
不會異步執行調用。每次調用都發生在調用線程中。它主要用于不需要多線程的情況。
ConcurrentTaskExecutor
Java 5 Java .util.concurrent. executor的包裝。替代方案是ThreadPoolTaskExecutor,它將Executor配置參數作為bean屬性公開。很少使用。
SimpleThreadPoolTaskExecutor
Quartz的SimpleThreadPool的一個子類,它監聽Spring的生命周期回調。Quartz組件和非Quartz組件共享需要共享一個線程池時,通常會使用這種方法。
ThreadPoolTaskExecutor
只能在java5中使用。公開了用于配置java.util.concurrent的bean屬性。如果需要高級的東西,比如ScheduledThreadPoolExecutor,建議使用ConcurrentTaskExecutor替代。
TimerTaskExecutor
通過TimerTask支撐實現。 不同于SyncTaskExecutor,因為方法調用在一個單獨的線程中執行,盡管它們在那個線程中是同步的。
WorkManagerTaskExecutor
使用CommonJ WorkManager作為它的支持實現,并且是在Spring上下文中設置CommonJ WorkManager引用的中心便利類。與SimpleThreadPoolTaskExecutor類似,這個類實現了WorkManager接口,因此也可以直接作為WorkManager使用。
2.2 使用 TaskExecutor
public class TaskExecutorExample { private class MessagePrinterTask implements Runnable { private String message; public MessagePrinterTask(String message) { this.message = message; } public void run() { System.out.println(message); } } private TaskExecutor taskExecutor; public TaskExecutorExample(TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; } public void printMessages() { for (int i = 0; i < 25; i++) { taskExecutor.execute(new MessagePrinterTask("Message" + i)); } } }
與其從池中檢索線程并自己執行,不如將Runnable添加到隊列中,而TaskExecutor使用其內部規則來決定任務何時執行
配置TaskExecutor將使用的規則
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent .ThreadPoolTaskExecutor"> <property name="corePoolSize" value="5" /> <property name="maxPoolSize" value="10" /> <property name="queueCapacity" value="25" /> </bean> <bean id="taskExecutorExample" class="cn.pconline.activity.task.TaskExecutorExample" init-method="printMessages"> <constructor-arg ref="taskExecutor" /> </bean>
三、TaskScheduler
除了任務執行者抽象之外。Spring 3.0還引入了一個TaskScheduler,它有多種方法來調度未來某個時候運行的任務。
public interface TaskScheduler { ScheduledFuture schedule(Runnable task, Trigger trigger); ScheduledFuture schedule(Runnable task, Date startTime); ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); ScheduledFuture scheduleAtFixedRate(Runnable task, long period); ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); }
名為“schedule”的方法,它只接受可運行的日期。這將導致任務在指定時間后運行一次。
所有其他方法都能夠安排任務重復運行。固定速率和固定延遲方法用于簡單的、周期性的執行,但是使用 Trigger 的方法要靈活得多。
3.1 Trigger
觸發器的基本思想是,執行時間可以根據過去的執行結果甚至任意條件來確定。如果這些決定確實考慮了前面執行的結果,那么該信息在TriggerContext中是可用的。
public interface Trigger { Date nextExecutionTime(TriggerContext triggerContext); } public interface TriggerContext { Date lastScheduledExecutionTime(); Date lastActualExecutionTime(); Date lastCompletionTime(); }
TriggerContext是最重要的部分。它封裝了所有相關的數據,如果有必要,將來還可以進行擴展。TriggerContext是一個接口(默認情況下使用SimpleTriggerContext實現)。
3.2 Trigger 實現
Spring提供了觸發器接口的兩個實現。最有趣的是CronTrigger。它支持基于cron表達式的任務調度。
scheduler.schedule(task, new CronTrigger(" 15 9-17 * MON-FRI"));
另一個開箱即用的實現是一個周期性Trigger ,它接受一個固定的周期、一個可選的初始延遲值,以及一個布爾值,用來指示周期應該解釋為固定速率還是固定延遲。由于TaskScheduler接口已經定義了以固定速率或固定延遲調度任務的方法,因此應該盡可能直接使用這些方法。PeriodicTrigger實現的價值在于它可以在依賴于觸發器抽象的組件中使用。例如,允許周期性Trigger 、基于cro的Trigger ,甚至自定義Trigger 實現可以互換使用,這可能很方便。這樣的組件可以利用依賴注入,這樣就可以在外部配置這樣的Trigger 。
3.3 TaskScheduler實現
在應用服務器環境中,TaskScheduler提供的靈活性尤其重要。因為在這種環境中,線程不應該由應用程序本身直接創建。對于這種情況,Spring提供了一個TimerManagerTaskScheduler,它將委托給CommonJ TimerManager實例,通常配置為JNDI-lookup。
四、調度和異步執行的注解支持
4.1 開啟scheduling 注解功能
為了支持@Scheduled和@Async注釋,請將@EnableScheduling和@EnableAsync添加到@Configuration類中
@Configuration @EnableAsync @EnableSCheduling public class AppConfig { }
你可以自由選擇應用程序的相關注釋。例如,如果只需要支持@Scheduled,那么只需省略@EnableAsync即可。對于更細粒度的控制,可以另外實現調度器和/或AsyncConfigurer接口。
如果你更喜歡xml配置,這樣配置。
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/> <task:executor id="myExecutor" pool-size="5"/> <task:scheduler id="myScheduler" pool-size="10"/>}
4.2 @Scheduled
@Scheduled添加到方法上
//上一次調用完之后,五秒再調用一次,依此循環下去 @Scheduled(fixedDelay=5000) public void doSomething() { // something that should execute periodically } //連續的每次調用開始時間之間,間隔5s @Scheduled(fixedRate=5000) public void doSomething() { // something that should execute periodically } // @Scheduled(cron="*/5 * * * * MON-FRI") public void doSomething() { // something that should execute on weekdays only }
值得注意的是調度的方法返回值必須是void,并且不能期望有任何參數。如果方法需要與來自應用程序上下文的其他對象交互,那么這些對象通常是通過依賴注入提供的。
4.3 @Async注解
添加了@Async注解的方法會異步執行。換句話說,方法調用后會立即返回,方法的實際執行將發生在提交給Spring TaskExecutor的任務中。
@Async void doSomething() { // this will be executed asynchronously }
/@Scheduled注釋的方法不同,這些方法可以預期參數. //因為調用方將在運行時以“正常”方式調用它們,而不是從容器管理的調度任務中調用。 @Async void doSomething(String s) { // this will be executed asynchronously }
// 具有Future回調返回值 //執行其它任務的優先級 依然是高于執行回調的優先級。 @Async Future<String> returnSomething(int i) { // this will be executed asynchronously }
4.4 指定@Async注解的執行器
默認情況下,在方法上指定@Async時,將使用的執行器是提供“annotation-driven”元素的執行器,如上所述。然而,當需要指示在執行給定方法時應該使用非默認的執行器時,可以使用@Async注釋的值屬性。
@Async("otherExecutor") void doSomething(String s) { // this will be executed asynchronously by "otherExecutor" }
五、Task 命名空間
從Spring 3.0開始,有一個用于配置TaskExecutor和TaskScheduler實例的XML名稱空間。并提供了一種方便的方法,可以將任務配置為使用觸發器進行調度。
5.1 scheduler 元素
<!-- 默認創建一個指定大小的ThreadPoolTaskScheduler --> <task:scheduler id="scheduler" pool-size="10"/>
id屬性用作線程池中線程的前綴名。如果不指定pool-size,默認的線程池中只有一個線程。
5.2 executor元素
<!-- 創建ThreadPoolTaskExecutor實例 --> <task:executor id="executor" pool-size="10"/>
與上面的調度器一樣,為'id'屬性提供的值將用作池中線程名稱的前綴。就池大小而言,'executor'元素比'scheduler'元素支持更多的配置選項。首先,ThreadPoolTaskExecutor的線程池本身是可配置的。執行程序的線程池可能對核心和最大大小有不同的值,而不僅僅是單個大小。如果提供了單個值,那么執行器將擁有一個固定大小的線程池(核心和最大大小相同)。然而,“executor”元素的“池大小”屬性也接受“min-max”形式的范圍。
<task:executor id="executorWithPoolSizeRange" pool-size="5-25" queue-capacity="100"/><!-- 指定隊列的大小-->
<task:executor id="executorWithCallerRunsPolicy" pool-size="5-25" queue-capacity="100" rejection-policy="CALLER_RUNS"/><!-- 任務拒絕策略(強制任務提交線程執行任務,從而限制傳入負載變得過大) 默認的是直接拋出異常-->
5.3 scheduled-tasks元素
Spring task namespace 最強大的特性是支持在Spring應用程序上下文中配置要調度的任務。這與Spring中的其他“方法調用者”類似,例如JMS名稱空間提供的配置消息驅動pojo的方法。
<task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/><!--ref 指向容器管理的對象,method是要執行的方法名--> </task:scheduled-tasks>
<task:scheduler id="myScheduler" pool-size="10"/> <task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/><!--第一次執行延遲一秒,以后每次上一個任務完成后 再執行--> <task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/> <!--固定頻率執行 如果上一個任務執行時間超時,第二個任務會立即執行 --> <task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/> </task:scheduled-tasks> <task:scheduler id="myScheduler" pool-size="10"/>
六、Quartz Scheduler
Quartz使用觸發器、作業和作業細節對象來實現各種作業的調度。有關Quartz背后的基本概念,請參閱http://quartz-scheduling er.org。為了方便起見,Spring提供了兩個類,它們簡化了基于Spring的應用程序中Quartz的使用。
6.1 使用JobDetailBean
JobDetail對象包含運行作業所需的所有信息。Spring框架提供了JobDetailBean,它使JobDetail更接近于具有合理默認值的實際JavaBean。
<bean name="exampleJob" class="org.springframework.scheduling .quartz.JobDetailBean"> <property name="jobClass" value="example.ExampleJob" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="5" /> </map> </property> </bean>
job detail bean 具有運行作業(ExampleJob)所需的所有信息。timeout在job data map指定。job data map可以通過JobExecutionContext(在執行時傳遞給您)獲得,但是JobDetailBean還將 job data map 中的屬性映射到實際job的屬性。因此,在本例中,如果ExampleJob包含一個名為timeout的屬性,JobDetailBean將自動應用它。
package example; public class ExampleJob extends QuartzJobBean { private int timeout; /** * Setter called after the ExampleJob is instantiated * with the value from the JobDetailBean (5) */ public void setTimeout(int timeout) { this.timeout = timeout; } protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { // do the actual work } }
6.2 使用MethodInvokingJobDetailFactoryBean
使用MethodInvokingJobDetailFactoryBean你可以調用特定對象上的方法。
<bean id="jobDetail" class="org.springframework.scheduling.quartz .MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> </bean>
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
使用上面的配置將會導致ExampleBusinessObject .doIt()方法被調用。
public class ExampleBusinessObject { // properties and collaborators public void doIt() { // do the actual work } }
使用MethodInvokingJobDetailFactoryBean,不再需要創建只調用一個方法的一行作業,只需要創建實際的業務對象并連接到它。
默認情況下,Quartz作業是無狀態的,導致作業相互干擾的可能性。如果為相同的JobDetail指定兩個觸發器,那么可能在第一個作業完成之前,第二個作業就會開始。如果JobDetail類實現有狀態接口,則不會發生這種情況。在第一項工作完成之前,第二項工作不會開始。要使方法調用jobdetailfactorybean產生的作業非并發,請將并發標志設置為false。
<bean id="jobDetail" class="org.springframework.scheduling.quartz .MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> <property name="concurrent" value="false" /> <!--并發執行false--> </bean>
6.3 作業調度
盡管我們能使用MethodInvokingJobDetailFactoryBean調用特定對象上的方法,但是我們還是需要調度作業 。這需要使用觸發器和scheduler erfactorybean完成。Quartz 提供了多種觸發器
Spring提供了兩種Quartz 工廠對象:
CronTriggerFactoryBean
SimpleTriggerFactoryBean
兩種觸發器的示例
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz .SimpleTriggerFactoryBean"> <!-- see the example of method invoking job above --> <property name="jobDetail" ref="jobDetail" /> <!-- 10 seconds --> <property name="startDelay" value="10000" /> <!-- repeat every 50 seconds --> <property name="repeatInterval" value="50000" /> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz .CronTriggerFactoryBean"> <property name="jobDetail" ref="exampleJob" /> <!-- run every morning at 6 AM --> <property name="cronExpression" value="0 0 6 * * ?" /> <!-- 每天早上六點執行--> </bean>
以上是“Spring任務執行和調度的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。