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