Spring的任務調度@Scheduled註解——task:scheduler和task:executor的解析

 

一個簡單的Spring定時任務的 demo,所有代碼見下載地址:https://download.csdn.net/download/yx0628/10511753 
對於 applicationContext 的配置以下:調度器線程池 task:scheduler 和 task:executor 的意義在後邊例子中會詳細的測試和說明。css

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd" xmlns:task="http://www.springframework.org/schema/task"> <context:annotation-config /> <task:annotation-driven scheduler="myScheduler" executor="myExecutor"/> <!-- 調度線程池配置 --> <task:scheduler id="myScheduler" pool-size="5"/> <!-- 執行線程池配置 --> <task:executor id="myExecutor" pool-size="5"/> <context:component-scan base-package="com.zaimeibian" /> </beans>a package com.zaimeibian.task; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class PrintTask { DateFormat df = new SimpleDateFormat("HH:mm:ss"); // 這個Async註解,表明當前任務是要異步執行的 @Async @Scheduled(fixedRate = 5000) public void printA(){ System.out.println("A執行 " + df.format(new Date())); try { Thread.sleep(10000); } catch (InterruptedException e) { } System.out.println("A打印輸出 " + df.format(new Date())+ Thread.currentThread()); } @Scheduled(fixedRate = 5000) public void printB(){ System.out.println("B執行 " + df.format(new Date())); try { Thread.sleep(10000); } catch (InterruptedException e) { } System.out.println("B打印輸出 " + df.format(new Date())+ Thread.currentThread()); } @Scheduled(fixedRate = 5000) public void printC(){ System.out.println("C執行 " + df.format(new Date())); try { Thread.sleep(10000); } catch (InterruptedException e) { } System.out.println("C打印輸出 " + df.format(new Date())+ Thread.currentThread()); } // 配置initialDelay的任務是在容器啓動後延遲必定時間纔開始調度 @Scheduled(fixedRate = 5000, initialDelay=1000) public void printD(){ System.out.println("D執行 " + df.format(new Date())); try { Thread.sleep(30000); } catch (InterruptedException e) { } System.out.println("D打印輸出 " + df.format(new Date())+ Thread.currentThread()); } // 配置initialDelay的任務是在容器啓動後延遲必定時間纔開始調度 @Scheduled(fixedRate = 5000, initialDelay=1000) public void printE(){ System.out.println("E執行 " + df.format(new Date())); try { Thread.sleep(30000); } catch (InterruptedException e) { } System.out.println("E打印輸出 " + df.format(new Date())+ Thread.currentThread()); } }

這裏,fixDelay 和 fixRate 參數表明每一個任務,前者在上一個任務調度完成後,延遲必定的時間執行。然後者能夠在每間隔必定時間就執行新任務(但這裏與 executor 的參數有關)。下面的試驗會詳細說明這兩個參數。 
Spring 的任務調度線程池,即java

<task:scheduler id="myScheduler" pool-size="5"/>

若是不配置,那麼默認值是 1 ,即結果是全部聲明的任務,都是串行執行的,好比代碼中的 A/B/C/D/E 五個任務,在默認值是 1 的狀況下,只能一個個串行來執行。不能出現並行的狀況。 
能夠將參數改成 1 ,運行,輸出以下:spring

B執行 12:16:36 B打印輸出 12:16:46Thread[myScheduler-1,5,main] A執行 12:16:46 A打印輸出 12:16:56Thread[myScheduler-1,5,main] C執行 12:16:56 C打印輸出 12:17:06Thread[myScheduler-1,5,main] D執行 12:17:06 D打印輸出 12:17:36Thread[myScheduler-1,5,main] E執行 12:17:36 E打印輸出 12:18:06Thread[myScheduler-1,5,main] B執行 12:18:06 B打印輸出 12:18:16Thread[myScheduler-1,5,main] A執行 12:18:16 A打印輸出 12:18:26Thread[myScheduler-1,5,main] C執行 12:18:26 C打印輸出 12:18:36Thread[myScheduler-1,5,main] D執行 12:18:36 D打印輸出 12:19:06Thread[myScheduler-1,5,main] E執行 12:19:06 E打印輸出 12:19:36Thread[myScheduler-1,5,main] B執行 12:19:36 B打印輸出 12:19:46Thread[myScheduler-1,5,main] A執行 12:19:46 A打印輸出 12:19:56Thread[myScheduler-1,5,main]

能夠看到只有 myScheduler-1 這一個調度線程來調度這五個任務,任務之間只能串行,即等待上個任務完成後釋放調度線程,而後調度線程才能調度執行下一個任務。shell

而後咱們還改回調度線程池 5 個線程池大小,運行:markdown

C執行 12:23:04 A執行 12:23:04 B執行 12:23:04 E執行 12:23:05 D執行 12:23:05 C打印輸出 12:23:14Thread[myScheduler-2,5,main] C執行 12:23:14 A打印輸出 12:23:14Thread[myScheduler-3,5,main] A執行 12:23:14 B打印輸出 12:23:14Thread[myScheduler-1,5,main] B執行 12:23:14 C打印輸出 12:23:24Thread[myScheduler-2,5,main] C執行 12:23:24 A打印輸出 12:23:24Thread[myScheduler-3,5,main] A執行 12:23:24 B打印輸出 12:23:24Thread[myScheduler-1,5,main] B執行 12:23:24 C打印輸出 12:23:34Thread[myScheduler-2,5,main] A打印輸出 12:23:34Thread[myScheduler-3,5,main] C執行 12:23:34 A執行 12:23:34 B打印輸出 12:23:34Thread[myScheduler-1,5,main] B執行 12:23:34 E打印輸出 12:23:35Thread[myScheduler-4,5,main] E執行 12:23:35 D打印輸出 12:23:35Thread[myScheduler-5,5,main] D執行 12:23:35

能夠看到,若是每一個任務都有一個調度線程來處理,那麼就是很理想的狀況,各個任務之間是並行的,互不干擾各自獨立,按照各自的時間來觸發。(能夠看到 1-5 這 5 個線程都在各自調度本身的任務) 
這裏還要注意一點,fixDelay 和 fixRate 看上去彷佛是同樣的,在每一個任務的調度線程中,都是必須上一個執行完畢後,等待配置的時間後,再開始下一次的執行。是否是 fixRate 參數不起做用呢?由於不是說 fixRate 是間隔必定時間執行,而不須要等待上一個任務執行完畢麼?多線程

這裏引入另外一個參數,能夠看任務 A 上方註釋掉的 @Async 註解:這個註解,表明能夠異步執行。異步執行的話,調度線程池就會不用當前調度線程來執行,而是交給 task:executor 這個執行線程池來執行。 
咱們來運行,這裏爲了更好的說明,咱們能夠把 A 的 fixRate 改成 2秒 ,看運行結果:併發

B執行 12:34:44 C執行 12:34:44 A執行 12:34:44 D執行 12:34:45 E執行 12:34:45 A執行 12:34:46 A執行 12:34:48 A執行 12:34:50 A執行 12:34:52 B打印輸出 12:34:54Thread[myScheduler-2,5,main] B執行 12:34:54 C打印輸出 12:34:54Thread[myScheduler-3,5,main] A打印輸出 12:34:54Thread[myExecutor-1,5,main] C執行 12:34:54 A執行 12:34:54 A打印輸出 12:34:56Thread[myExecutor-2,5,main] A執行 12:34:56 A打印輸出 12:34:58Thread[myExecutor-3,5,main] A執行 12:34:58 A打印輸出 12:35:00Thread[myExecutor-4,5,main] A執行 12:35:00 A打印輸出 12:35:02Thread[myExecutor-5,5,main] A執行 12:35:02 B打印輸出 12:35:04Thread[myScheduler-2,5,main] B執行 12:35:04 C打印輸出 12:35:04Thread[myScheduler-3,5,main] A打印輸出 12:35:04Thread[myExecutor-1,5,main] C執行 12:35:04 A執行 12:35:04 A打印輸出 12:35:06Thread[myExecutor-2,5,main]

這裏 A 任務的線程是 myExecutor-1 到 myExecutor-5,說明 myScheduler-1 這個調度線程調度了 A 任務,可是交給了線程池中的 myExecutor 中的執行線程來具體執行的。 
因此,配置 task:scheduler 參數的線程池,是爲了根據任務總數來分配調度線程池的大小;而配置 task:executor ,是爲了某個任務若是要異步的執行時,實現當前任務內的多線程併發。app

相關文章
相關標籤/搜索