مهمة التوقيت هي وظيفة لتحديد نطاق زمني مستقبلي لأداء مهام معينة. في تطبيقات الويب الحالية ، تحتوي معظم التطبيقات على وظائف جدولة المهام. لديهم بناء جملة وحلول لأصوات وأنظمة التشغيل المختلفة. يطلق نظام تشغيل Windows على تخطيط المهام ، وتقدم خدمات CRON في Linux هذه الوظيفة. غالبًا ما تشارك هذه الوظيفة في نظام تطوير الأعمال لدينا. ستستخدم هذه الدردشة لغة Java لإكمال استخدام مهام التوقيت الشائعة الاستخدام في أعمال التنمية اليومية ، على أمل مساعدة الجميع على العمل والدراسة.
1. سيناريوهات المهمة المحددة
(1) قيادة سير العمل معالجة
كطلب جديد مسبق ، يتم تهيئته ووضعه. إذا لم يتم دفع الطلب خلال الوقت المحدد ، فسيتم اعتبار أن ترتيب المهلة سيتم إغلاقه. يستخدم على نطاق واسع في نظام التجارة الإلكترونية. يقوم المستخدمون بشراء البضائع وإنشاء الطلبات ولكن لا يقومون بالمدفوعات. إذا لم يتم دفع الطلب في غضون 30 دقيقة ، فسيتم إغلاق الطلب (وعدد الأشخاص الذين يستوفون هذا السيناريو ضخم) ، ومن المستحيل استخدام التدخل اليدوي.
(2) صيانة النظام
سيحصل عمل الإرسال على سجل استثناء النظام وتخزين بعض بيانات النقطة الرئيسية في قاعدة البيانات. سيتم إلقاؤه على قاعدة البيانات في الساعة 11:30 مساءً كل يوم من أيام الأسبوع (باستثناء الإجازات وأيام الأسبوع) ، وإنشاء ملف XML وإرساله إلى عنوان البريد الإلكتروني لموظف معين.
(3) تقديم خدمات التذكير داخل التطبيق.
يذكر النظام بانتظام المستخدم المسجل في أداء العمل ذي الصلة في وقت معين.
(4) مهام المصالحة المحددة
ستقوم الشركة والشركات ذات الأحزاب الثلاث (المشغلين والبنوك وما إلى ذلك) بإجراء تسوية للشركة في نفس اليوم بعد منتصف الليل كل يوم ، وإرسال بيانات نتائج معلومات التسوية إلى عنوان البريد الإلكتروني للشخص المعني المسؤول ، ومعالجة بيانات عدم التوافق خلال ساعات العمل في اليوم التالي.
(5) إحصاءات البيانات
هناك العديد من سجلات البيانات ، وسوف تولد القراءة والاستعلام في الوقت الفعلي من قاعدة البيانات قدرًا معينًا من الوقت ، وهو ما يخص تجربة العملاء واحتياجات الأداء. لذلك ، يتم تلخيص البيانات كل أسبوع (أيام ، ساعات) ، بحيث يمكن تقديم البيانات بسرعة عند عرض البيانات.
هناك العديد من السيناريوهات التي يتم فيها استخدام مهام التوقيت ... يبدو أن مهام التوقيت تستخدم على نطاق واسع في تطورنا اليومي ...
2. شرح توقيت تكنولوجيا مهام التوقيت السائدة
أعتقد أن الجميع على دراية بالفعل بـ java.util.timer. إنها أسهل طريقة لتنفيذ جدولة المهام. هنا مثال محدد:
حزمة com.ibm.scheduler ؛ استيراد java.util.timer ؛ استيراد java.util.timertask ؛ الطبقة العامة TimerTest يمتد timertask {private string jobname = "" ؛ Public MimerTest (String JobName) {super () ؛ this.jobname = JobName ؛ } Override public void run () {system.out.println ("Execute" + JobName) ؛ } public static void main (string [] args) {timer timer = new timer () ؛ تأخير طويل 1 = 1 * 1000 ؛ فترة طويلة 1 = 1000 ؛ // بعد ثانية واحدة من الآن ، قم بتنفيذ Timer1. تأخير طويل 2 = 2 * 1000 ؛ فترة طويلة 2 = 2000 ؛ // بعد ثانيتين من الآن ، قم بتنفيذ Timer.Schedule (New TimerTest ("Job2") ، Delay2 ، الفترة 2) ؛ }} /**
نتيجة الإخراج:
تنفيذ Job1
تنفيذ Job1
تنفيذ Job2
تنفيذ Job1
تنفيذ Job1
تنفيذ Job2
*/
الفئات الأساسية التي تستخدم المؤقت لتنفيذ جدولة المهام هي مؤقت و timertask. الموقت مسؤول عن تعيين وقت تنفيذ البدء والفاصل الزمني لـ TimerTask. يحتاج المستخدم فقط إلى إنشاء فئة ميراث من timertask ، وتنفيذ طريقة التشغيل الخاصة به ، ثم رميها إلى المؤقت للتنفيذ. إن تصميم Timer Core هو قائمة المهام ومهمة. يرمي المؤقت المهام المستلمة في قائمة المهام الخاصة بها ، والتي تقوم بفرز قائمة المهام وفقًا لوقت التنفيذ الأولي للمهمة. يبدأ الموقت في أن يصبح خيطًا خفيًا عند إنشاء مؤقت. استطلاعات الخيط هذه جميع المهام ، ويجد مهمة يتم تنفيذها مؤخرًا ، ثم ينام. عندما يتم إيقاف وقت بدء تنفيذ الموقت مؤخرًا ، يتم إيقاظ المؤقت ويتم تنفيذ المهمة. بعد ذلك ، يقوم TimerThread بتحديث آخر مهمة يتم تنفيذها وتواصل السبات.
تتمثل ميزة الموقت في أنها بسيطة وسهلة الاستخدام ، ولكن نظرًا لأن جميع المهام يتم جدولةها بواسطة نفس الخيط ، يتم تنفيذ جميع المهام بشكل متسلسل ، ويمكن تنفيذ مهمة واحدة فقط في نفس الوقت. سيؤثر تأخير أو استثناء المهمة السابقة على المهام اللاحقة (يجب الانتباه إلى هذه النقطة).
SchedeDexecutor
في ضوء أوجه القصور المذكورة أعلاه من المؤقت ، أطلقت Java 5 SchedeDexecutor استنادًا إلى تصميم تجمع الخيوط. فكرة التصميم هي أنه سيتم تنفيذ كل مهمة مجدولة بواسطة مؤشر ترابط في مجموعة مؤشرات الترابط ، وبالتالي يتم تنفيذ المهام بشكل متزامن ولن يتم إزعاجها من قبل بعضها البعض. تجدر الإشارة إلى أن ScreedExecutor لن يبدأ بالفعل مؤشر ترابط فقط عندما يأتي وقت تنفيذ المهمة ، ويقوم ScreedExecutor بالاقتراع للمهمة لبقية الوقت.
package com.ibm.scheduler ؛ import java.util.concurrent.executors ؛ import java.util.concurrent.scheduledexecutorservice ؛ استيراد java.util.concurrent.timeUnit ؛ public classexecutortestistrest runnable {private str. Public SchededExecutortest (String JobName) {super () ؛ this.jobname = JobName ؛ } Override public void run () {system.out.println ("Execute" + JobName) ؛ } public static void main (string [] args) {ScripedExecutorService service = eversors.newscheduledThreadPool (10) ؛ initialDelay1 = 1 ؛ فترة طويلة 1 = 1 ؛ // بعد ثانية واحدة من الآن فصاعدًا ، قم بتنفيذ Service. initialdelay2 = 1 ؛ تأخير طويل 2 = 1 ؛ // بعد ثانيتين من الآن فصاعدًا ، تنفيذ Service.SchedulewithFixedDelay (New SchedeDexecutortest ("Job2") ، initialDelay2 ، Develop2 ، TimeUnit.Seconds) ؛ }}/** نتيجة الإخراج: تنفيذ Job1Execute Job1Execute Job2Execute Job1Execute Job1Execute Job2*/
يعرض الرمز أعلاه طريقتين جدولة أكثر استخدامًا ، ScheduleAtfixedrate و ScheduleWeWithFixedDelay ، في ScheduleDelta. ScheduleAtFixedRate كل وقت تنفيذ هو فترة زمنية للوقوع مرة أخرى من بداية المهمة السابقة ، أي أن كل وقت تنفيذ هو: initialdelay ، initialdelay+الفترة ، initialDelay+2* initialDelay+2*ExecuteTime+2*Delay. يمكن ملاحظة أن ScheduleAtFixedRate يعتمد على فترات زمنية ثابتة لجدولة المهام ، ويعتمد ScheduleWithFixedDelay على طول كل وقت تنفيذ المهمة ويستند إلى فترات زمنية غير مثبتة لجدولة المهام.
استخدم SchededExecutor والتقويم لتنفيذ جدولة المهام المعقدة
لا يمكن لكل من المؤقت و SchedeDexecutor توفير جدولة المهام إلا على أساس وقت البدء وفاصل التكرار ، وليست مؤهلة لمتطلبات الجدولة الأكثر تعقيدًا. على سبيل المثال ، قم بتعيين المهمة المراد تنفيذها كل يوم ثلاثاء الساعة 16:38:10. لا يمكن تنفيذ هذه الوظيفة بشكل مباشر باستخدام Timer أو SchededExecutor ، ولكن يمكننا تنفيذها بشكل غير مباشر بمساعدة التقويم.
package com.ibm.scheduler ؛ استيراد java.util.calendar ؛ استيراد java.util.date ؛ استيراد java.util.timertask ؛ java.util.concurrent.executors يمتد TimerTask {private string jobname = "" ؛ Public ScheduleDexceUtortest2 (String JobName) {super () ؛ this.jobname = JobName ؛ } Override public void run () {system.out.println ("date ="+new Date ()+"، execute"+JobName) ؛ } /** * احسب آخر مرة تبدأ من الوقت الحالي الحالي وتلتقي مع الشروط في كل يوم ، ساعة من يوم ، * minuteofhour ، secondofminite * @return * /calendar public geteArliestDate (Calendar currentdate ، int dayofweek ، intof day ، int int secondofminite) { hour_of_day ، دقيقة ، ثانيًا ، إلخ int currentdayofweek = currentDate.get (calendar.day_of_week) ؛ int currenthour = currentDate.get (calendar.hour_of_day) ؛ int currentminute = currentDate.get (calendar.minute) ؛ int currentsecond = currentDate.get (calendar.Second) ؛ // إذا كان DayOfweek في حالة الإدخال أقل من يوم اليوم من التاريخ الحالي ، فيجب تأجيل Week_of_year بمقدار أسبوع واحد Boolean Weeklater = false ؛ if (dayofweek <currentdayofweek) {weeklater = true ؛ } آخر إذا (dayofweek == currentdayofweek) {// عندما تكون حالة الإدخال مساوية لـ DayOfweek من التاريخ الحالي ، إذا كان // hourfday في حالة الإدخال أقل من // currenthour من التاريخ الحالي ، يجب تأجيل Week_of_year بمقدار أسبوع واحد إذا (hourfday <currenthour) {weeklater = true ؛ } آخر إذا (hourfday == currenthour) {// عندما تكون حالة الإدخال مساوية لـ DayOfweek ، يوم ساعة من التاريخ الحالي ، // إذا كانت دقيقة في حالة الإدخال أقل من // currentminute للتاريخ الحالي ، يجب تأجيل Week_of_year بمقدار أسبوع واحد إذا (minuteofhour <currentminute) {weeklater = true ؛ } آخر إذا (minuteofhour == currentsecond) {// عندما تكون حالة الإدخال مساوية لـ DayOfweek ، Hourfday ، // minuteofhour ، if // secondofminite في حالة الإدخال أقل من currentsecond من الموعد الحالي ، // then week_of_year يجب أن يتم تأجيله لمدة أسبوع واحد إذا (secondofminite <currentsecond) }}} ستر } // set day_of_week ، hour_of_day ، دقيقة ، ثانية في التاريخ الحالي كقيم في حالة الإدخال. CurrentDate.set (calendar.day_of_week ، dayofweek) ؛ CurrentDate.set (calendar.hour_of_day ، hourfday) ؛ CurrentDate.set (calendar.minute ، minuteofhour) ؛ CurrentDate.set (Calendar.Second ، SecondOfminite) ؛ إرجاع CurrentDate ؛ } static void main (string [] args) يلقي استثناء {sclodedExCeUtorTest2 test = جديد ScripedExceUtortest2 ("Job1") ؛ // الحصول على الوقت الحالي CurrentDate = Calendar.getInstance () ؛ currentDatelong long = currentDate.getTime (). getTime () ؛ System.out.println ("Current Date =" + CurrentDate.getTime (). ToString ()) ؛ // احسب آخر وقت تنفيذ يفي تقويم الشرط المبكر = test. long -spileDatelong = earlyDate.getTime (). getTime () ؛ System.out.println ("DateLiest Date =" + earlyDate.getTime (). toString ()) ؛ . // حساب فترة التنفيذ هي فترة طويلة فترة طويلة = 7 * 24 * 60 * 60 * 1000 ؛ SignedExecutorService Service = Executors.NewScheduledThreadPool (10) ؛ // من الآن تأخير milliseconds ، تنفيذ job1 service.ScheDuleAtfixedrate (الاختبار ، التأخير ، الفترة ، timeunit.milliseconds) ؛ }}/** نتيجة الإخراج: التاريخ الحالي = الأربعاء فبراير 02 17:32:01 CST 2011Earliest Date = Tue February 8 16:38:10 CST 2011date = Tue February 8 16:38:10 CST 2011 ، Execute Job1Date = Tue February 15 16:38:10 CST 2011 ، Execute Job1*/
ينفذ الرمز أعلاه وظيفة مهام الجدولة في الساعة 16:38:10 كل يوم ثلاثاء. جوهر هو حساب الوقت المطلق من 16:38:10 يوم الثلاثاء الماضي استنادًا إلى الوقت الحالي ، ثم حساب الفرق الزمني من الوقت الحالي كمعلمة للاتصال بوظيفة SchedeDexceutor. لحساب آخر وقت ، يتم استخدام وظيفة java.util.calendar. أولاً ، نحتاج إلى شرح بعض أفكار التصميم للتقويم. يحتوي التقويم على المجموعات التالية التي تحدد تاريخًا فريدًا:
يقتبس
السنة + الشهر + day_of_month
السنة + الشهر + week_of_month + day_of_week
السنة + الشهر + day_of_week_in_month + day_of_week
سنة + day_of_year
السنة + day_of_week + week_of_year
يتم الجمع بين المجموعات أعلاه مع ساعة من يوم + دقيقة + ثانية لتكون علامة زمنية كاملة.
يعتمد العرض التوضيحي أعلاه طريقة التركيبة الأخيرة. الإدخال هو day_of_week ، hour_of_day ، الدقيقة ، والثانية والتاريخ الحالي ، والمخرج هو تاريخ مستقبلي يرضي day_of_week ، hour_of_day ، دقيقة ، ثانية وأقرب إلى التاريخ الحالي. مبدأ الحساب هو البدء في المقارنة من Day_of_week. إذا كان أقل من Day_of_week من التاريخ الحالي ، فأنت بحاجة إلى زيادة زيادة الأسبوع _oof_year ، أي إضافة week_of_year في التاريخ الحالي والكتابة فوق القيمة القديمة ؛ إذا كان مساوياً لـ Day_of_week الحالي ، فاستمر في مقارنة Hour_of_day ؛ إذا كان أكبر من Day_of_week الحالي ، اتصل مباشرةً بوظيفة Calendar.set (الحقل ، القيمة) لـ java.util.calenda لتعيين Day_of_week ، hour_of_day ، دقيقة ، ثانية إلى قيمة الإدخال ، وما إلى ذلك حتى يتم الوصول إلى المقارنة إلى الثانية. يمكننا تحديد مجموعات مختلفة بناءً على متطلبات الإدخال لحساب أحدث وقت للتنفيذ.
من المهم تمامًا تنفيذ جدولة المهام باستخدام الطريقة أعلاه. نأمل أن نحتاج إلى أداة جدولة مهام أكثر اكتمالا لحل مشكلات الجدولة المعقدة هذه. لحسن الحظ ، أظهرت مجموعة أدوات المصدر المفتوحة الكوارتز قدرات رائعة في هذا الصدد.
كوارتز
OpenSymphony Open Source Organization هي مشروع مفتوح المصدر آخر في مجال جدولة الوظائف ، والتي يمكن دمجها مع تطبيقات J2EE و J2SE أو المستخدمة وحدها. يمكن استخدام الكوارتز لإنشاء برامج بسيطة أو معقدة تدير عشرة أو مئات أو حتى عشرات الآلاف من الوظائف.
دعونا نلقي نظرة على مثال:
حزمة com.test.quartz ؛ استيراد org.quartz.datebuilder.newdate java.util.gregoriancalendar ؛ استيراد org.quartz.jobdetail ؛ استيراد org.quartz.scheduler ؛ استيراد org.quartz.trigger Main (string [] args) {try {// إنشاء Scheduler Scheduler = StdScheDulerFactory.getDefaultScheduler () ؛ // تحديد مشغل الزناد الزناد = newTrigger (). with internity ("trigger1" ، "group1") // تعريف الاسم/المجموعة .startnow () // بمجرد إضافة مجدول ، فإنه يأخذ على الفور. // ينفذ بشكل أساسي ، وهرع حتى لا يتم إيقافه. build () ؛ // تحديد وظيفة JobDetail JobDetail = NewJob (helloQuartz.class) // حدد فئة الوظيفة على أنها فئة HelloQuartz ، وهي منطق التنفيذ الحقيقي. // أضف إلى هذا المجدول. // start Scheduler.start () ؛ // مغلق thread.sleep (10000) ؛ Scheduler.Shutdown (true) ؛ } catch (استثناء e) {E.PrintStackTrace () ؛ }}} package com.test.quartz ؛ import java.util.date ؛ import org.quartz.disallowConcurrentExecution ؛ import org.quartz.job ؛ import org.quartz.jobdetail ؛ Execute (JobexecutionContext Context) يلقي JobexecutionException {JobDetail Detail = context.getJobDetail () ؛ اسم السلسلة = التفاصيل. getJobDatamap (). System.out.println ("قل مرحبا إلى" + name + "في" + New Date ()) ؛ }}من خلال الأمثلة المذكورة أعلاه: العناصر الأساسية الثلاثة الأكثر أهمية في الكوارتز:
• جدولة: جدولة. يتم التحكم في جميع الجدولة.
• الزناد: يحدد حالة التشغيل. في المثال ، يكون نوعه Simpletrigger ، الذي يتم تنفيذه كل ثانية واحدة (ما هو Simpletrigger ، سيتم وصفه بالتفصيل أدناه).
• JobDetail & Job: يحدد JobDetail بيانات المهمة ، ويقدم منطق التنفيذ الحقيقي في الوظيفة ، في المثال ، HelloQuartz. لماذا تم تصميمه كوظيفة Jobdetail + ، ولا تستخدم الوظيفة مباشرة؟ هذا لأنه من الممكن تنفيذ المهام بشكل متزامن. إذا كان المجدول يستخدم الوظائف مباشرة ، فستكون هناك مشكلة في الوصول المتزامن إلى نفس مثيل الوظيفة. في طريقة Jobdetail & Job ، في كل مرة يتم تنفيذ Sheruler ، ستنشئ مثيلًا جديدًا للوظيفة استنادًا إلى dobbetail ، بحيث يمكن تجنب مشكلة الوصول المتزامن.
Quartz API
يكون نمط API الخاص بـ Quartz بعد 2.x ، ويعتمد نمط DSL (يعني عادة نمط الواجهة بطلاقة) ، وهو جزء NewTrigger () في المثال. يتم تنفيذها من خلال البناء ، وهو ما يلي. (تشير معظم الرموز التالية إلى هؤلاء البناة)
// builderport static org.quartz.jobbuilder org.quartz.dailytimeintervalscheduleBuilder.*؛ استيراد org.quartz.calendarintervalscheduleBuilder.*؛ قارن: JobDetail JobDetail = new JobDetailimpl ("JobDetail1" ، "Group1" ، HelloQuartz.Class) ؛ JobDetail.getJobDatamap (). put ("name" ، "Quartz") ؛ SimpleTrigGerPpl trigger = new SimperTrigpl ("trigger1" ، "Group1") ؛ DATE ()عن الاسم والمجموعة
Jobdetail و Trigger لها اسم ومجموعة.
الاسم هو معرفهم الفريد في هذا sheduler. إذا أردنا تحديث تعريف Jobdetail ، نحتاج فقط إلى تعيين مثيل dobdetail مع نفس الاسم.
Group هي وحدة تنظيم ، وستوفر Sheduler بعض واجهات برمجة التطبيقات لمجموعة العمليات بأكملها ، مثل Scheduler.ResumeJobs ().
مشغل
قبل البدء في شرح كل مشغل بالتفصيل ، تحتاج إلى فهم بعض القواسم المشتركة من الزناد.
وقت البدء ووقت الانتهاء
سيتم تشغيل الفاصل الزمني المحدد في وقت البدء والوقت. خارج هذا الفاصل ، لن يتم تشغيل الزناد. جميع المشغلات سوف تحتوي على هاتين الخصائص.
أولوية
عندما يكون المجدول مشغولاً ، قد يتم تشغيل مشغلات متعددة في نفس الوقت ، ولكن الموارد غير كافية (مثل تجمع الخيوط غير الكافي). ثم ، في هذا الوقت ، هناك طريقة أفضل من قطعة قماش مقص الحجرية هي وضع الأولوية. تنفيذ الأولوية أولاً. تجدر الإشارة إلى أن الأولوية ستعمل فقط بين المشغلات التي تم تنفيذها في نفس الوقت ، إذا كان أحد المشغلات هو الساعة 9:00 والآخر هو 9:30. ثم بغض النظر عن مدى ارتفاع الأولوية التالية ، سيتم تنفيذ أولوية السابقة أولاً. القيمة الافتراضية للأولوية هي 5 ، ويتم استخدام القيمة الافتراضية عندما تكون سلبية. لا يبدو أن القيمة القصوى محددة ، ولكن يوصى باتباع معيار Java واستخدام 1-10. خلاف ذلك ، إذا كنت تعرف ما إذا كانت هناك قيمة أكبر عليها عندما ترى [الأولوية هي 10].
استراتيجية اختلال (Miss Trigger)
عندما تكون موارد جدولة مماثلة غير كافية ، أو عندما تتعطل الجهاز وإعادة التشغيل ، فمن المحتمل ألا يتم تشغيل بعض المشغلات في الوقت الذي يجب أن يتم فيه تشغيلها ، أي تفوت النار. في هذا الوقت ، يحتاج Trigger إلى استراتيجية للتعامل مع هذا الموقف. تختلف الاستراتيجيات الاختيارية لكل مشغل. فيما يلي نقطتان للانتباه إلى:
يتمتع Misfire Trigger بقيمة عتبة ، تم تكوينها في Jobstore. أكثر من Ramjobstore هو org.quartz.jobstore.misfirethreshold. لن يتم حساب الخطأ إلا على أنه يتجاوز هذا العتبة. أقل من هذه العتبة ، سيتم إعادة ترجمة الكوارتز. جميع استراتيجيات الاختلاف في الواقع الإجابة على سؤالين:
• هل يجب إعادة تشغيل هذا الخاطئ؟
• في حالة حدوث اختلال ، هل تريد ضبط وقت الجدولة الحالي؟
على سبيل المثال ، تشمل استراتيجية Simpletrigger اختلال:
• misfire_instruction_ignore_misfire_policy هذا لا يعني تجاهل الزناد الفائت ، بل تجاهل سياسة الاختلال. سيسترجع جميع مهام الاختلال عندما يكون المورد مناسبًا ولن يؤثر على وقت الجدولة الحالي. على سبيل المثال ، يتم تنفيذ Simpletrigger كل 15 ثانية ، ولديه 5 دقائق في الوسط ويفتقد 20 مفقودًا. بعد 5 دقائق ، على افتراض أن الموارد كافية وأن المهمة تسمح بالتزامن ، سيتم تشغيلها في وقت واحد. هذه الخاصية تنطبق على جميع المشغلات.
• misfire_instruction_fire_now تجاهل المهام التي كانت خاطئا وأداء الجداول الزمنية على الفور. هذا عادة ما ينطبق فقط على المهام التي يتم تنفيذها مرة واحدة فقط.
• misfire_instruction_reschedule_now_with_existing_repeat_count قم بتعيين وقت البدء على الوقت الحالي وإعادة جدولة المهمة على الفور ، بما في ذلك الاختلال.
• misfire_instruction_reschedule_now_with_remaining_repeat_count على غرار misfireinstructionReschedulenowWithExistingRepeat_Count ، فإن الفرق هو أن المهام التي تم بالفعل تجاهلها.
• misfire_instruction_reschedule_next_with_existing_count في الوقت المقرر التالي ، أعد تشغيل مهمة الإرسال ، بما في ذلك الخطأ.
• misfire_instruction_reschedule_next_with_remaining_count على غرار MisfireinStructionReschedulenextWithExistingCount ، فإن الفرق هو أن المهام التي تم بالفعل تجاهلها.
• misfire_instruction_smart_policy جميع القيم الافتراضية الخاطئة للمشغلات هي ، مما يعني تقريبًا "اترك منطق المعالجة إلى الكوارتز الذكي لتقريره." الاستراتيجية الأساسية هي.
• إذا تم تنفيذ الجدول الزمني مرة واحدة فقط ، فاستخدم misfire_instruction_fire_now.
• إذا كانت جدولة لا حصر لها (REPERCOUNT غير محدود) ، استخدم MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMING_COUNT.
• خلاف ذلك ، باستخدام misfire_instruction_reschedule_now_with_existing_repeat_count غير معقد للغاية ، يمكنك الرجوع إلى هذه المقالة.
تقويم
التقويم هنا ليس java.util.calendar من JDK ، وليس لحساب التواريخ. وظيفتها هي استكمال وقت الزناد. يمكن استبعاد بعض النقاط المحددة في الوقت المناسب.
أخذ "السداد التلقائي لديون البطاقات في الساعة 0:00 في 25 من كل شهر" ، على سبيل المثال ، نريد أن نستبعد نقطة زمنية من 25 فبراير من كل عام (لأن هناك 2.14 ، سوف يفلس فبراير بالتأكيد). هذه المرة يمكن تحقيقها بواسطة التقويم.
مثال:
السنوي Cal = NewCalendar () ؛ // تحديد التقويم الذي يتم تنفيذه كل عام بدقة أيام ، أي أنه لا يمكن تعريفه حتى الساعة 2:00 مساءً في 2.25 java.util.calendar isscleday = new gregoriancalendar () ؛ excludeday.settime (newDate (). . // Scheduler ينضم إلى هذا التقويم // تحديد مشغل triggertrigger = newTrigger (). مع "Trigger1" ، "Group1") .StartNow () // بمجرد إضافة SCHEDULER ، فإنه يسري على الفور. .
يوفر لنا كوارتز التقويمات التالية. لاحظ أن جميع التقويمات يمكن أن تكون إما استبعاد أو إدراج بناءً على:
• HolidayCalendar. حدد تاريخًا محددًا ، مثل 20140613. دقة السماء.
• DailyCalendar. يحدد الفترة الزمنية من كل يوم (RangeStartingTime ، RangeendingTime) ، التنسيق هو HH: MM [: SS [: MMMM]]. وهذا يعني أن الحد الأقصى للدقة يمكن أن تصل إلى ميلي ثانية.
• Weeklycalendar. حدد يوم الأسبوع من كل أسبوع ، والقيمة الاختيارية هي java.util.calendar.sunday. الدقة هي اليوم.
• شهري. حدد يوم كل شهر. القيم الاختيارية هي 1-31. الدقة هي اليوم
• السنوي. حدد أي يوم من العام. طريقة الاستخدام كما هو موضح في المثال أعلاه. الدقة هي اليوم.
• croncalendar. يحدد تعبير كرون. تعتمد الدقة على تعبير cron ، أي أن الحد الأقصى للدقة يمكن أن تصل إلى ثوان.
فئة التنفيذ الزناد
الكوارتز لديه تطبيقات الزناد التالية:
Simpletrigger
يحدد المهام التي تبدأ في وقت معين ويتم تنفيذها في فترة زمنية معينة (بالميلي ثانية). إنه مناسب للمهام المشابهة لـ: ابدأ في الساعة 9:00 ، وتنفيذ كل ساعة واحدة. خصائصها هي:
• تكرار تكرار الفاصل الزمني
• تكرار عدد التكرار. العدد الفعلي لعمليات الإعدام هو REPERCOUNT+1. لأنه سيتم تنفيذها مرة واحدة في وقت البدء. وينطبق الشيء نفسه على خاصية REPERTCOUNT أدناه.
مثال:
التقويم
على غرار Simpletrigger ، فإنه يحدد المهام التي تبدأ في وقت معين ويتم تنفيذها في فترة زمنية معينة. ولكن الفرق هو أن الفاصل الزمني المحدد من قبل Simpletrigger هو ميلي ثانية ، ولا توجد طريقة لتحديد أنه سيتم تنفيذه كل شهر (الفاصل الشهري ليس قيمة ثابتة) ، في حين تتضمن وحدات الفاصل الزمني المدعوم من التقويم التقويم ثواني ، دقائق ، ساعات ، أيام ، شهور ، وسنوات ، وأسابيع. بالمقارنة مع Simpletrigger ، فإنه له مزايزان: 1. إنه أكثر ملاءمة. على سبيل المثال ، إذا قمت بتنفيذ كل ساعة واحدة ، فلن تضطر إلى حساب عدد المللي ثانية 1 ساعة مساوية. 2. يدعم الفواصل الزمنية غير الثابتة ، مثل الفواصل الزمنية التي هي أشهر وسنوات. لكن العيب هو أن الدقة لا يمكن أن تصل إلا إلى ثوان. تشبه مهمتها المناسبة: ابدأ في الساعة 9:00 وتنفيذ مرة واحدة كل أسبوع في الساعة 9:00. خصائصها هي:
• الفاصل الزمني للتنفيذ
• فاصل زمني للفاصل الزمني للتنفيذ (ثواني ، دقائق ، ساعات ، أيام ، شهور ، سنوات ، أسابيع)
مثال:
CalendarIntervalsChedule (). withintervalindays (1) // تنفيذ مرة واحدة يوميًا.
DailyTimeIntervaltrigger
حدد أن المهام يتم تنفيذها على فترات معينة خلال فترة زمنية معينة كل يوم. ويمكن أن تدعم أسابيع محددة. تشبه مهمتها المناسبة: تحديد من 9:00 إلى 18:00 كل يوم ، يتم تنفيذه كل 70 ثانية ، ومن الاثنين إلى الجمعة فقط. خصائصها هي:
• وقت البدء في يوم البدء كل يوم
• وقت نهاية اليوم نهاية اليوم
• أيام الأسبوع في الأسبوع المراد تنفيذه
• الفاصل الزمني للتنفيذ
• فاصل زمني للفاصل الزمني للتنفيذ (ثواني ، دقائق ، ساعات ، أيام ، شهور ، سنوات ، أسابيع)
• تكرار عدد التكرارات
مثال:
DailyTimeIntervalsChedule () .StartingDailyat (timeofday.hourandminuteofday (9 ، 0)) // ابدأ في الساعة 9:00 في اليوم. EndingDailyat (timeofday.hourandminuteofday (16 ، 0)) // end at 16: 00.ondaysoekeeek (الاثنين ، الثلاثاء ، يوم الخميس ، يوم الجمعة). Intertervalinhours (1) // تنفيذ كل ساعة واحدة. withrepeatcount (100) // كرر ما يصل إلى 100 مرة (تم تنفيذه بالفعل 100+1 مرة) .build () ؛ DailyTimeIntervalSchedule () .startingDailyat (timeofday.hourandminuteofday (9 ، 0)) // ابدأ في 9:00 في اليوم. تقوم هذه الطريقة في الواقع بحساب endtimeofday استنادًا إلى starttimeofday+الفاصل الزمني*.
crontrigger
مناسبة للمهام الأكثر تعقيدًا ، فإنه يدعم بناء الجملة المكتوبة إلى Linux CRON (وهو أكثر قوة). في الأساس ، يغطي معظم المشغلات الثلاثة المذكورة أعلاه - بالطبع ، من الصعب أيضًا فهمها. تشبه مهامها المناسبة: يتم تنفيذ كل يوم كل يوم في الساعة 0:00 و 9:00 و 18:00. خصائصها هي فقط:
تعبيرات كرون
لكن هذا التمثيل نفسه معقد بما فيه الكفاية. سيكون هناك تعليمات أدناه. مثال:
cronschedule ("0 0/2 8-17 * *؟") // تنفيذ كل دقيقتين في 8: 00-17: 00 كل يوم. build () ؛ cronschedule ("0 30 9؟ * Mon .Build () ؛تعبيرات كرون
| موقع | مجال الوقت | القيم المسموح بها | قيمة خاصة |
| 1 | ثانية | 0-59 | ، - * / |
| 2 | دقيقة | 0-59 | ، - * / |
| 3 | ساعة | 0-23 | ، - * / |
| 4 | تاريخ | 1-31 | ، - *؟ / LWC |
| 5 | شهر | 1-12 | ، - * / |
| 6 | أسبوع | 1-7 | ، - *؟ / LC # |
| 7 | سنة (اختيارية) | 1-31 | ، - * / |
• النجمة (): يمكن استخدامها في جميع الحقول لتمثيل كل لحظة في مجال الوقت المقابل ، على سبيل المثال ، في حقل الدقيقة ، يعني "في الدقيقة" ؛
• علامة الاستفهام (؟): يتم استخدام هذه الشخصية فقط في حقول التاريخ والأسبوع ، وعادة ما يتم تحديدها على أنها "قيمة لا معنى لها" ، أي ما يعادل حرف DOT ؛
• ناقص علامة (-): يعبر عن نطاق. إذا تم استخدام "10-12" في حقل الساعة ، فهذا يعني من 10 إلى 12 نقطة ، أي 10،11،12 ؛
• فواصل (،): يعبر عن قيمة قائمة. إذا تم استخدام "Mon ، Wed ، Fri" في حقل الأسبوع ، فهذا يعني الاثنين والأربعاء والجمعة ؛
• Slash (/): تمثل x/y تسلسلًا متساويًا ، x هي قيمة البداية و y هي قيمة الخطوة الإضافية. إذا كنت تستخدم 0/15 في حقل الدقيقة ، فسيتم التعبير عنها على أنها 0 و 15 و 30 و 45 ثانية ، في حين أن 5/15 تعني 5 و 20 و 35 و 50 في حقل الدقيقة ، يمكنك أيضًا استخدام */Y ، أي ما يعادل 0/Y ؛
• L: يتم استخدام هذه الشخصية فقط في حقول التاريخ والأسبوع ، والتي تمثل معنى "الأخير" ، ولكن هذا يعني بشكل مختلف في الحقلين. يشير L في حقل التاريخ إلى اليوم الأخير من الشهر ، مثل 31 يناير و 28 فبراير ، وهو ليس سنة قفزة ؛ إذا تم استخدام L في الأسبوع ، فهذا يشير إلى يوم السبت ، وهو ما يعادل 7. ومع ذلك ، إذا ظهر L في حقل الأسبوع وسببه قيمة X ، فهذا يعني "آخر X Days of the Month" ، على سبيل المثال ، 6L تعني يوم الجمعة الأخير من الشهر ؛
• W: لا يمكن أن تظهر هذه الشخصية إلا في حقل التاريخ وهي تعديل للتاريخ الرئيسي ، مما يشير إلى أن يوم العمل الأقرب إلى التاريخ. على سبيل المثال ، يمثل 15W أقرب يوم عمل إلى 15 من الشهر. إذا كان 15 من الشهر يوم السبت ، فإنه يتطابق يوم الجمعة 14 ؛ إذا كان 15 من الشهر يوم الأحد ، فإنه يتطابق يوم الاثنين 16 ؛ إذا كان 15 من الشهر يوم الثلاثاء ، فهو الثلاثاء 15. ومع ذلك ، تجدر الإشارة إلى أنه لا يمكن عبور تاريخ المطابقة إلى الشهر. إذا حددت 1W ، إذا كان اليوم الأول هو يوم السبت ، فإن النتيجة تتطابق مع الاثنين 3 ، وليس اليوم الأخير من الشهر الماضي. يمكن لسلسلة W تحديد تاريخ واحد فقط ، ولكن لا يمكن تحديد نطاق تاريخ ؛
• مجموعة LW: يمكن استخدام LW في حقل التاريخ ، مما يعني آخر يوم عمل من الشهر ؛ علامة الجنيه (#): لا يمكن استخدام هذه الشخصية إلا في حقل الأسبوع ، الذي يمثل يوم عمل من الشهر. على سبيل المثال ، يمثل 6#3 الجمعة الثالثة من الشهر (يمثل 6 يوم الجمعة ، رقم 3 يمثل الثالث في الوقت الحالي) ، في حين يمثل 4#5 الأربعاء الخامس من الشهر ، على افتراض أن الشهر ليس لديه يوم الأربعاء الخامس ، ولا يتم تجاهله ولم يتم تشغيله ؛
• C: يتم استخدام هذه الشخصية فقط في حقول التاريخ والأسبوع ، والتي تمثل معنى "التقويم". وهذا يعني أن التاريخ المرتبط بالخطة ، وإذا كان التاريخ غير مرتبط ، فهو يعادل جميع التواريخ في التقويم. على سبيل المثال ، 5C في حقل التاريخ يعادل اليوم الأول بعد اليوم الخامس من التقويم. 1C يعادل اليوم الأول بعد يوم الأحد في حقل الأسبوع.
تعبيرات CRON ليست حساسة لحالة الشخصيات الخاصة وليست حساسة للاختصار لحالة اللغة الإنجليزية لهذا الأسبوع. بعض الأمثلة:
| تعبير | يوضح |
| 0 0 12 * *؟ | ركض في الساعة 12 كل يوم |
| 0 15 10؟ * * | ركض الساعة 10:15 كل يوم |
| 0 15 10 * *؟ | ركض الساعة 10:15 كل يوم |
| 0 15 10 * *؟ * | ركض الساعة 10:15 كل يوم |
| 0 15 10 * *؟ 2008 | الركض في الساعة 10:15 في اليوم في عام 2008 |
| 0 * 14 * *؟ | يمتد كل دقيقة بين الساعة 14:00 وينتهي في الساعة 14:59 كل يوم. |
| 0 0/5 14 * *؟ | ركض كل 5 دقائق من الساعة 14:00 إلى 15:00 كل يوم ، بدءًا من الساعة 14:55 وينتهي في الساعة 14:55. |
| 0 0/5 14،18 * *؟ | يستمر كل 5 دقائق من الساعة 14:00 إلى 15:00 كل يوم ، ويستمر كل 5 دقائق من الساعة 18:00 إلى 19:00 كل يوم. |
| 0-5 14 * *؟ | ركض كل دقيقة من الساعة 14:00 إلى 14:05 كل يوم. |
| 0 10،44 14؟ 3 الأربعاء | 3月每周三的14:10分到14:44,每分钟运行一次。 |
| 0 15 10 ? * MON-FRI | 每周一,二,三,四,五的10:15分运行。 |
| 0 15 10 15 * ? | 每月15日10:15分运行。 |
| 0 15 10 L * ? | 每月最后一天10:15分运行。 |
| 0 15 10 ? * 6L | 每月最后一个星期五10:15分运行。 |
| 0 15 10 ? * 6L 2007-2009 | 在2007,2008,2009年每个月的最后一个星期五的10:15分运行。 |
| 0 15 10 ? * 6#3 | 每月第三个星期五的10:15分运行。 |
JobDetail & Job
JobDetail是任务的定义,而Job是任务的执行逻辑。在JobDetail里会引用一个Job Class定义。一个最简单的例子:
public class JobTest { public static void main(String[] args) throws SchedulerException, IOException { JobDetail job=newJob() .ofType(DoNothingJob.class) //Check Job Class .withIdentity("job1", "group1") //Set name/group .withDescription("this is a test job") //Set description.usingJobData("age", 18) //Add attributes to ageJobDataMap .build(); job.getJobDataMap().put("name", "quertz"); //Add attribute name to JobDataMap //Define a SimpleTrigger trigger that executes once per second. Trigger trigger=newTrigger().startNow().withIdentity("trigger1") .withSchedule(simpleSchedule().withIntervalInSeconds(1) .repeatForever()) .build(); Scheduler sche=StdSchedulerFactory.getDefaultScheduler(); sche.scheduleJob(job, trigger); sche.start(); System.in.read(); sche.shutdown(); }}public class DoNothingJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("do nothing"); }}从上例我们可以看出,要定义一个任务,需要干几件事:
•创建一个org.quartz.Job的实现类,并实现实现自己的业务逻辑。比如上面的DoNothingJob。
•定义一个JobDetail,引用这个实现类
•加入scheduleJob Quartz调度一次任务,会干如下的事:
•JobClass jobClass=JobDetail.getJobClass()
•Job jobInstance=jobClass.newInstance()。所以Job实现类,必须有一个public的无参构建方法。
•jobInstance.execute(JobExecutionContext context)。JobExecutionContext是Job运行的上下文,可以获得Trigger、Scheduler、JobDetail的信息。
也就是说,每次调度都会创建一个新的Job实例,这样的好处是有些任务并发执行的时候,不存在对临界资源的访问问题――当然,如果需要共享JobDataMap的时候,还是存在临界资源的并发访问的问题。
JobDataMap
Job是newInstance的实例,那我怎么传值给它? 比如我现在有两个发送邮件的任务,一个是发给"liLei",一个发给"hanmeimei",不能说我要写两个Job实现类LiLeiSendEmailJob和HanMeiMeiSendEmailJob。实现的办法是通过JobDataMap。
每一个JobDetail都会有一个JobDataMap。JobDataMap本质就是一个Map的扩展类,只是提供了一些更便捷的方法,比如getString()之类的。
我们可以在定义JobDetail,加入属性值,方式有二:
•newJob().usingJobData("age", 18) //加入属性到ageJobDataMap
•job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap
然后在Job中可以获取这个JobDataMap的值,方式同样有二:
public class HelloQuartz implements Job { private String name; public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); JobDataMap map = detail.getJobDataMap(); //Method 1: Obtain JobDataMap System.out.println("say hello to " + name + "[" + map.getInt("age") + "]" + " at " + new Date()); } //Method 2: The setter method of the property will automatically inject the JobDataMap attribute into public void setName(String name) { this.name = name; }}对于同一个JobDetail实例,执行的多个Job实例,是共享同样的JobDataMap,也就是说,如果你在任务里修改了里面的值,会对其他Job实例(并发的或者后续的)造成影响。
除了JobDetail,Trigger同样有一个JobDataMap,共享范围是所有使用这个Trigger的Job实例。
Job并发
Job是有可能并发执行的,比如一个任务要执行10秒中,而调度算法是每秒中触发1次,那么就有可能多个任务被并发执行。
Sometimes we don’t want to execute tasks concurrently, for example, this task needs to "get a list of all unsent mails in the database". If it is executed concurrently, a database lock is needed to avoid a data being processed multiple times. This time a @DisallowConcurrentExecution solves this problem. هذا كل شيء:
public class DoNothingJob implements Job { @DisallowConcurrentExecution public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("do nothing"); }}注意,@DisallowConcurrentExecution是对JobDetail实例生效,也就是如果你定义两个JobDetail,引用同一个Job类,是可以并发执行的。
JobExecutionException
Job.execute()方法是不允许抛出除JobExecutionException之外的所有异常的(包括RuntimeException),所以编码的时候,最好是try-catch住所有的Throwable,小心处理。
其他属性
•Durability(耐久性?) 如果一个任务不是durable,那么当没有Trigger关联它的时候,它就会被自动删除。
•RequestsRecovery 如果一个任务是"requests recovery",那么当任务运行过程非正常退出时(比如进程崩溃,机器断电,但不包括抛出异常这种情况),Quartz再次启动时,会重新运行一次这个任务实例。
可以通过JobExecutionContext.isRecovering()查询任务是否是被恢复的。
Scheduler
•Scheduler就是Quartz的大脑,所有任务都是由它来设施。
•Schduelr包含一个两个重要组件: JobStore和ThreadPool。
•JobStore是会来存储运行时信息的,包括Trigger,Schduler,JobDetail,业务锁等。它有多种实现RAMJob(内存实现),JobStoreTX(JDBC,事务由Quartz管理),JobStoreCMT(JDBC,使用容器事务),ClusteredJobStore(集群实现)、TerracottaJobStore(什么是Terractta)。
•ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务的都会由线程池执行。
SchedulerFactory
SchdulerFactory,顾名思义就是来用创建Schduler了,有两个实现:DirectSchedulerFactory和StdSchdulerFactory。前者可以用来在代码里定制你自己的Schduler参数。后者是直接读取classpath下的quartz.properties(不存在就都使用默认值)配置来实例化Schduler。通常来讲,我们使用StdSchdulerFactory也就足够了。
SchdulerFactory本身是支持创建RMI stub的,可以用来管理远程的Scheduler,功能与本地一样,可以远程提交个Job什么的。DirectSchedulerFactory的创建接口:
/** * Same as * {@link DirectSchedulerFactory#createScheduler(ThreadPool threadPool, JobStore jobStore)}, * with the addition of specifying the scheduler name and instance ID. This * scheduler can only be retrieved via * {@link DirectSchedulerFactory#getScheduler(String)} * * @param schedulerName * The name for the scheduler. * @param schedulerInstanceId * The instance ID for the scheduler. * @param threadPool * The thread pool for executing jobs * @param jobStore * The type of job store * @throws SchedulerException * if initialization failed */ public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore) throws SchedulerException;StdSchdulerFactory的配置例子, 更多配置,参考Quartz配置指南:
org.quartz.scheduler.instanceName = DefaultQuartzSchedulerorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = trueorg.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
三、Quartz 集成Spring
开发一个job类,普通java类,需要有一个执行的方法:
package com.tgb.lk.demo.quartz;import java.util.Date;public class MyJob { public void work() { System.out.println("date:" + new Date().toString()); }}把类放到spring容器中,可以使用配置也可以使用注解:
<bean id="myJob" />
配置jobDetail,指定job对象:
<!-- 配置jobDetail,指定job对象--> <bean id="accountJobDetail"> <property name="targetObject"> <ref bean="accountJob" /> </property> <property name="targetMethod"> <value>work</value> </property> </bean>
配置一个trigger,需要指定一个cron表达式,指定任务的执行时机:
<!-- accountTrigger 的配置--> <bean id="accountTrigger" > <property name="jobDetail"> <ref bean="accountJobDetail" /> </property> <property name="cronExpression"> <value>0/3 * * * * ?</value> </property> </bean>
配置调度工厂:
<!-- 启动触发器的配置开始--> <bean name="startQuertz" lazy-init="false" autowire="no" > <property name="triggers"> <list> <ref bean="myJobTrigger" /> </list> </property> </bean> <!-- 启动触发器的配置结束-->
项目启动,定时器开始执行。
四、分析不同定时任务优缺点,寻找一种符合你项目需求的定时任务Timer管理延时任务的缺陷
以前在项目中也经常使用定时器,比如每隔一段时间清理项目中的一些垃圾文件,每隔一段时间进行日志清理;然而Timer是存在一些缺陷的,因为Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,会发生一些缺陷
Timer当任务抛出异常时的缺陷
如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行
Timer执行周期任务时依赖系统时间
Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。
对异常的处理
Quartz的某次执行任务过程中抛出异常,不影响下一次任务的执行,当下一次执行时间到来时,定时器会再次执行任务;而TimerTask则不同,一旦某个任务在执行过程中抛出异常,则整个定时器生命周期就结束,以后永远不会再执行定时器任务。
精确到和功能
Quartz每次执行任务都创建一个新的任务类对象,而TimerTask则每次使用同一个任务类对象。 Quartz可以通过cron表达式精确到特定时间执行,而TimerTask不能。Quartz拥有TimerTask所有的功能,而TimerTask则没有上述,基本说明了在以后的开发中尽可能使用ScheduledExecutorService(JDK1.5以后)替代Timer。
五、cron 在线表达式生成器http://cron.qqe2.com/附录cron 表达式
cron表达式用于配置cronTrigger的实例。cron表达式实际上是由七个子表达式组成。这些表达式之间用空格分隔。
•Seconds (秒)
•Minutes(分)
•Hours(小时)
•Day-of-Month (天)
•Month(月)
•Day-of-Week (周)
•Year(年)
例:"0 0 12 ? * WED” 意思是:每个星期三的中午12点执行。个别子表达式可以包含范围或者列表。例如:上面例子中的WED可以换成"MON-FRI","MON,WED,FRI",甚至"MON-WED,SAT"。子表达式范围:
•Seconds (0~59)
•Minutes (0~59)
•Hours (0~23)
•Day-of-Month (1~31,但是要注意有些月份没有31天)
•Month (0~11,或者"JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV,DEC")
•Day-of-Week (1~7,1=SUN 或者"SUN, MON, TUE, WED, THU, FRI, SAT”)
•Year (1970~2099)
Cron表达式的格式:秒分时日月周年(可选)。
字段名| 允许的值| 允许的特殊字符------- | ------ | ------ | ------ 秒| 0-59 | , - * / 分| 0-59 | , - * / 小时| 0-23 | , - * / 日| 1-31 | , - * ? / LWC 月| 1-12 or JAN-DEC | , - * / 周几| 1-7 or SUN-SAT | , - * ? / LC # 年(可选字段) | empty 1970-2099 | , - * /
字符含义:
•*:代表所有可能的值。因此,“*”在Month中表示每个月,在Day-of-Month中表示每天,在Hours表示每小时
•-:表示指定范围。
•,:表示列出枚举值。例如:在Minutes子表达式中,“5,20”表示在5分钟和20分钟触发。
•/:被用于指定增量。例如:在Minutes子表达式中,“0/15”表示从0分钟开始,每15分钟执行一次。"3/20"表示从第三分钟开始,每20分钟执行一次。和"3,23,43"(表示第3,23,43分钟触发)的含义一样。
•?:用在Day-of-Month和Day-of-Week中,指“没有具体的值”。当两个子表达式其中一个被指定了值以后,为了避免冲突,需要将另外一个的值设为“?”。例如:想在每月20日触发调度,不管20号是星期几,只能用如下写法:0 0 0 20 * ?,其中最后以为只能用“?”,而不能用“*”。
•L:用在day-of-month和day-of-week字串中。它是单词“last”的缩写。它在两个子表达式中的含义是不同的。
•在day-of-month中,“L”表示一个月的最后一天,一月31号,3月30号。
•在day-of-week中,“L”表示一个星期的最后一天,也就是“7”或者“SAT”
•如果“L”前有具体内容,它就有其他的含义了。例如:“6L”表示这个月的倒数第六天。“FRIL”表示这个月的最后一个星期五。
•注意:在使用“L”参数时,不要指定列表或者范围,这样会出现问题。
•W:“Weekday”的缩写。只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日,即最后一个星期五。
•# :只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3" or "FRI#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
表达式例子:
0 * * * * ? 每1分钟触发一次
0 0 * * * ? 每天每1小时触发一次
0 0 10 * * ? 每天10点触发一次
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 30 9 1 * ? 每月1号上午9点半
0 15 10 15 * ? 每月15日上午10:15触发
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0 12 ? * WED 表示每个星期三中午12点
0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
The above timed tasks (example explanation) in Java implementation web applications are all the content I share with you. آمل أن تتمكن من إعطائك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.