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