首先要端正一下本人的態度,開發了很長時間的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註解能夠指定方法,在被調用時,以異步的方式執行。