1、StatefulJob
implements StatefulJob使Job成為有狀態(tài)的,順序執(zhí)行
2、MethodInvokingJobDetailFactoryBean
implements StatefulJob使Job成為有狀態(tài)的,順序執(zhí)行
同一個有狀態(tài)的job實例不存在并發(fā),無狀態(tài)的job的并發(fā)數(shù)由上面配置的線程數(shù)決定。不想并發(fā)的話,設(shè)置成1,第二個線程在前一個執(zhí)行完以后觸發(fā)執(zhí)行。
線程數(shù)大于1時,如果存在空閑線程,則到執(zhí)行時間點即觸發(fā)執(zhí)行。
2、MethodInvokingJobDetailFactoryBean
MethodInvokingJobDetailFactoryBean的并發(fā)問題
大家在使用quartz的時候,一般只設(shè)置了“targetObject”和“targetMethod”,MethodInvokingJobDetailFactoryBean類默認(rèn)是并發(fā)執(zhí)行的,這時候如果不設(shè)置“concurrent”為false,很可能帶來并發(fā)或者死鎖的問題,而且?guī)茁瘦^小,不容易復(fù)現(xiàn),請大家使用的時候注意設(shè)置“concurrent”。
<bean id="cpm.MessageJobFactoryBean" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="cpm.MessageJob"/>
<property name="targetMethod" value="execute"/>
<property name="concurrent" value="false"/>
</bean>
concurrent 同時發(fā)生
concurrent:對于相同的JobDetail,當(dāng)指定多個Trigger時, 很可能第一個job完成之前,第二個job就開始了。
定concurrent設(shè)為false,多個job不會并發(fā)運行,第二個job將不會在第一個job完成之前開始
防止job并行運行的幾種解決方案
一、JOB State
在通過MethodInvokingJobDetailFactoryBean在運行中動態(tài)生成的Job,配置的xml文件有個concurrent屬性,表示job是否可以并行運行:如果一個job的業(yè)務(wù)處理發(fā)費的時間超過了job的啟動的間隔時間(repeatInterval),這個屬性非常有用。如果為false,那么,在這種情況下,當(dāng)前job還在運行,那么下一個job只能延時運行。如果為true,那么job就會并行運行。在實際的應(yīng)用中應(yīng)該配置為true/false,要根據(jù)需要了(廢話)。
二、如果通過繼承QuartzJobBean實現(xiàn)job的話,默認(rèn)情況下QuartzJobBean是implements org.quartz.Job接口的,也就是說job示例是stateless的,會出現(xiàn)前面所述的并行情況。而代碼中卻要求job任務(wù)必需串行,解決辦法:在job子類中繼續(xù)implements org.quartz.StatefulJob。那么這個job實例變成了Stateful,job任務(wù)也就是串行的了。
注:
在Quartz中,如果實現(xiàn)org.quartz.Job接口,那么這個job是stateless的,job實例的參數(shù)不能在多個任務(wù)之間共享,如果實現(xiàn)org.quartz.StatefulJob,這個job是個單例的,job實例的屬性可以從當(dāng)前任務(wù)傳遞到下一個任務(wù)。
spring和quartz的整合對版本是有要求的。
spring3.1以下的版本必須使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然會出錯。
至于原因,則是spring對于quartz的支持實現(xiàn),org.springframework.scheduling.quartz.CronTriggerBean繼承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是個類,而在quartz2.x系列中org.quartz.CronTrigger變成了接口,從而造成無法用spring的方式配置quartz的觸發(fā)器(trigger)。
MethodInvokingJobDetailFactoryBean中concurrent和shouldRecover屬性的作用
解釋 concurrent為true,則允許一個QuartzJob并發(fā)執(zhí)行,否則就是順序執(zhí)行。例如QuartzJob A執(zhí)行時間為15秒,配置為每10秒執(zhí)行一次;如果concurrent為true,則0秒的時候啟動一次A,10秒的時候再啟動一次A,20秒的時候再啟動一次A,不管前面啟動的A有沒有執(zhí)行完;如果concurrent為false,則0秒的時候啟動一次A,15秒的時候A執(zhí)行完畢,再第二次啟動A。
shouldRecover屬性為true,則當(dāng)Quartz服務(wù)被中止后,再次啟動或集群中其他機(jī)器接手任務(wù)時會嘗試恢復(fù)執(zhí)行之前未完成的所有任務(wù)。例如QuartzJob B,在每次00秒的時候啟動,假如在03:00的任務(wù)執(zhí)行完之后服務(wù)器1被中止,服務(wù)器2在05:15的時候才接手;如果shouldRecover屬性為true,則服務(wù)器2會嘗試著補回原來在04:00和05:00的時候應(yīng)該做的任務(wù),如果shouldRecover屬性為false,則服務(wù)器2只會從06:00的時候再執(zhí)行B。
在Spring中使用Quartz有兩種方式實現(xiàn):第一種是任務(wù)類繼承QuartzJobBean,第二種則是在配置文件里定義任務(wù)類和要執(zhí)行的方法,類和方法可以是普通類。很顯然,第二種方式遠(yuǎn)比第一種方式來的靈活。
MethodInvokingJobDetailFactoryBean中concurrent和shouldRecover屬性的作用
解釋 concurrent為true,則允許一個QuartzJob并發(fā)執(zhí)行,否則就是順序執(zhí)行。例如QuartzJob A執(zhí)行時間為15秒,配置為每10秒執(zhí)行一次;如果concurrent為true,則0秒的時候啟動一次A,10秒的時候再啟動一次A,20秒的時候再啟動一次A,不管前面啟動的A有沒有執(zhí)行完;如果concurrent為false,則0秒的時候啟動一次A,15秒的時候A執(zhí)行完畢,再第二次啟動A。
shouldRecover屬性為true,則當(dāng)Quartz服務(wù)被中止后,再次啟動或集群中其他機(jī)器接手任務(wù)時會嘗試恢復(fù)執(zhí)行之前未完成的所有任務(wù)。例如QuartzJob B,在每次00秒的時候啟動,假如在03:00的任務(wù)執(zhí)行完之后服務(wù)器1被中止,服務(wù)器2在05:15的時候才接手;如果shouldRecover屬性為true,則服務(wù)器2會嘗試著補回原來在04:00和05:00的時候應(yīng)該做的任務(wù),如果shouldRecover屬性為false,則服務(wù)器2只會從06:00的時候再執(zhí)行B。
Quartz集群只支持JDBCJobStore存儲方式,而MethodInvokingJobDetailFactoryBean不能序列化存儲job數(shù)據(jù)到數(shù)據(jù)庫,
重寫 quartz 的 QuartzJobBean 類
原因是在使用 quartz+spring 把 quartz 的 task 實例化進(jìn)入數(shù)據(jù)庫時,會產(chǎn)生: serializable 的錯誤,原因在于:
這個 MethodInvokingJobDetailFactoryBean 類中的 methodInvoking 方法,是不支持序列化的,因此在把 QUARTZ 的 TASK 序列化進(jìn)入數(shù)據(jù)庫時就會拋錯。網(wǎng)上有說把 SPRING 源碼拿來,修改一下這個方案,然后再打包成 SPRING.jar 發(fā)布,這些都是不好的方法,是不安全的。
必須根據(jù) QuartzJobBean 來重寫一個自己的類 。