งานเวลาเป็นฟังก์ชั่นของการระบุช่วงเวลาในอนาคตเพื่อทำงานบางอย่าง ในเว็บแอปพลิเคชันปัจจุบันแอปพลิเคชันส่วนใหญ่มีฟังก์ชั่นการจัดตารางงาน พวกเขามีไวยากรณ์ของตัวเองและโซลูชั่นสำหรับเสียงและระบบปฏิบัติการที่แตกต่างกัน ระบบปฏิบัติการ Windows เรียกว่าการวางแผนงานและบริการ cron ใน Linux ให้ฟังก์ชั่นนี้ ฟังก์ชั่นนี้มักเกี่ยวข้องกับระบบการพัฒนาธุรกิจของเรา การแชทนี้จะใช้ภาษา Java เพื่อให้การใช้งานเวลาที่ใช้กันทั่วไปในงานพัฒนาประจำวันโดยหวังว่าจะช่วยให้ทุกคนทำงานและศึกษา
1. สถานการณ์งานที่หมดเวลา
(1) เวิร์กโฟลว์การประมวลผลไดรฟ์
เป็นคำสั่งซื้อล่วงหน้าใหม่จะมีการเริ่มต้นและวางไว้ หากคำสั่งซื้อไม่ได้ชำระภายในเวลาที่กำหนดจะได้รับการพิจารณาว่าคำสั่งหมดเวลาจะถูกปิด มีการใช้กันอย่างแพร่หลายในระบบอีคอมเมิร์ซ ผู้ใช้ซื้อสินค้าและสร้างคำสั่งซื้อ แต่ไม่ชำระเงิน หากคำสั่งซื้อไม่ได้ชำระภายใน 30 นาทีคำสั่งจะถูกปิด (และจำนวนผู้คนที่ประชุมสถานการณ์นี้มีขนาดใหญ่มาก) และเป็นไปไม่ได้ที่จะใช้การแทรกแซงด้วยตนเอง
(2) การบำรุงรักษาระบบ
งานจัดส่งจะได้รับบันทึกข้อยกเว้นระบบและจัดเก็บข้อมูลจุดสำคัญบางอย่างในฐานข้อมูล มันจะถูกทิ้งลงในฐานข้อมูลเวลา 23:30 น. ทุกวันธรรมดา (ยกเว้นวันหยุดและวันธรรมดา) และสร้างไฟล์ XML และส่งไปยังที่อยู่อีเมลของพนักงานคนหนึ่ง
(3) ให้บริการเตือนความจำภายในแอปพลิเคชัน
ระบบเตือนผู้ใช้ที่เข้าสู่ระบบให้ทำงานที่เกี่ยวข้องเป็นประจำในเวลาที่กำหนด
(4) งานคืนดีที่กำหนดเวลา
บริษัท และ บริษัท สามพรรค (ผู้ประกอบการธนาคาร ฯลฯ ) จะดำเนินการกระทบยอดธุรกิจในวันเดียวกันหลังเที่ยงคืนทุกวันส่งข้อมูลผลการกระทบยอดไปยังที่อยู่อีเมลของบุคคลที่เกี่ยวข้องและประมวลผลข้อมูลที่ไม่ตรงกันในช่วงเวลาทำงานของวันถัดไป
(5) สถิติข้อมูล
มีบันทึกข้อมูลมากมายและการอ่านและการสืบค้นตามเวลาจริงจากฐานข้อมูลจะสร้างเวลาที่กำหนดซึ่งสำหรับประสบการณ์ของลูกค้าและความต้องการประสิทธิภาพ ดังนั้นข้อมูลจะถูกสรุปทุกสัปดาห์ (วัน, ชั่วโมง) เพื่อให้ข้อมูลสามารถนำเสนอได้อย่างรวดเร็วเมื่อแสดงข้อมูล
มีหลายสถานการณ์ที่ใช้งานเวลา ... ดูเหมือนว่างานเวลาจะใช้กันอย่างแพร่หลายในการพัฒนาประจำวันของเรา ...
2. คำอธิบายเกี่ยวกับการจับเวลาเทคโนโลยีเวลาหลัก
ฉันเชื่อว่าทุกคนคุ้นเคยกับ java.util.timer อยู่แล้ว มันเป็นวิธีที่ง่ายที่สุดในการใช้งานการจัดตารางงาน นี่คือตัวอย่างเฉพาะ:
แพ็คเกจ com.ibm.scheduler; นำเข้า java.util.timer; นำเข้า java.util.timertask; TimerTest คลาสสาธารณะขยาย TimerTask {Private String JobName = ""; timertest สาธารณะ (string jobName) {super (); this.jobname = JobName; } @Override โมฆะสาธารณะ Run () {System.out.println ("Execute" + JobName); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ตัวจับเวลาตัวจับเวลา = ตัวจับเวลาใหม่ (); Long Delay 1 = 1 * 1000; ระยะเวลายาว 1 = 1,000; // หลังจาก 1 วินาทีจากนี้ให้ดำเนินการ Timer.Schedule (TimerTest ใหม่ ("JOB1"), delay1, period1); Long Delay2 = 2 * 1000; ระยะเวลายาว 2 = 2000; // หลังจาก 2 วินาทีจากนี้ให้ดำเนินการ Timer.Schedule (TimerTest ใหม่ ("JOB2"), delay2, period2); - -
ผลลัพธ์ผลลัพธ์:
ดำเนินการ Job1
ดำเนินการ Job1
ดำเนินการ Job2
ดำเนินการ Job1
ดำเนินการ Job1
ดำเนินการ Job2
-
คลาสหลักที่ใช้ตัวจับเวลาเพื่อใช้การกำหนดเวลางานคือตัวจับเวลาและ TimerTask ตัวจับเวลามีหน้าที่ในการตั้งค่าเวลาเริ่มต้นและช่วงเวลาการดำเนินการของ Timertask ผู้ใช้ต้องการเพียงสร้างคลาสการสืบทอดของ Timertask ใช้วิธีการเรียกใช้ของตัวเองจากนั้นโยนมันไปยังตัวจับเวลาเพื่อดำเนินการ แกนกลางการออกแบบของตัวจับเวลาเป็นรายการงานและ taskthread ตัวจับเวลาโยนงานที่ได้รับลงในรายการงานซึ่งเรียงลำดับรายการงานตามเวลาดำเนินการเริ่มต้นของงาน TimerThread เริ่มเป็นเธรด daemon เมื่อสร้างตัวจับเวลา เธรดนี้สำรวจงานทั้งหมดพบงานที่เพิ่งจะดำเนินการเมื่อเร็ว ๆ นี้แล้วนอนหลับ เมื่อเวลาเริ่มต้นของการดำเนินการล่าสุด TimerThread จะถูกปลุกและงานจะถูกดำเนินการ หลังจากนั้น TimerThread จะอัปเดตงานล่าสุดที่จะดำเนินการและยังคงไฮเบอร์เนต
ข้อได้เปรียบของตัวจับเวลาคือมันง่ายและใช้งานง่าย แต่เนื่องจากงานทั้งหมดถูกกำหนดโดยเธรดเดียวกันงานทั้งหมดจึงถูกดำเนินการอย่างต่อเนื่องและงานเดียวเท่านั้นที่สามารถดำเนินการได้ในเวลาเดียวกัน ความล่าช้าหรือข้อยกเว้นของงานก่อนหน้านี้จะส่งผลกระทบต่องานที่ตามมา (จุดนี้จะต้องได้รับความสนใจ)
ScheduleDexecutor
ในมุมมองของข้อบกพร่องข้างต้นของตัวจับเวลา Java 5 ได้เปิดตัว ScheduleDexecutor ตามการออกแบบพูลเธรด แนวคิดการออกแบบคือแต่ละงานที่กำหนดเวลาจะถูกดำเนินการโดยเธรดในพูลเธรดดังนั้นงานจะถูกดำเนินการพร้อมกันและจะไม่ถูกรบกวนซึ่งกันและกัน ควรสังเกตว่า ScheduedExecutor จะเริ่มต้นเธรดจริง ๆ เมื่อเวลาดำเนินการของงานมาถึงและ ScheduedExecutor กำลังสำรวจงานตลอดเวลาที่เหลือ
แพ็คเกจ com.ibm.scheduler; นำเข้า java.util.concurrent.executors; นำเข้า java.util.concurrent.scheduledexecutorservice; นำเข้า java.util.concurrent.timeunit; Public ScheduleDexecutortest (string jobName) {super (); this.jobname = JobName; } @Override โมฆะสาธารณะ Run () {System.out.println ("Execute" + JobName); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {scheduleDexecutorservice service = executors.newscheduledThreadPool (10); Long Initialdelay1 = 1; ระยะเวลายาว 1 = 1; // หลังจาก 1 วินาทีจากนี้ไปให้ดำเนินการ Job1 Service.ScheduleatFixedrate (ScheduleDexecutortest ใหม่ ("Job1"), Initialdelay1, Period1, TimeUnit.Seconds); Long Initialdelay2 = 1; ล่าช้ายาว 2 = 1; // หลังจาก 2 วินาทีต่อจากนี้ให้ดำเนินการ Job2 Service.ScheduleWithFixedDelay (ScheduleDexecutortest ใหม่ ("Job2"), Initialdelay2, Delay2, TimeUnit.Seconds); -/** ผลลัพธ์ผลลัพธ์: ดำเนินการ Job1Execute Job1Execute Job2Execute Job1Execute Job1Execute Job2*//
รหัสด้านบนแสดงวิธีการกำหนดเวลาที่ใช้กันมากที่สุดสองวิธีคือ scheduleatfixedrate และ scheduleWithIthFixedDelay ใน ScheduleDelta ScheduleatFixedrate แต่ละครั้งเวลาดำเนินการเป็นช่วงเวลาที่ผลักกลับจากจุดเริ่มต้นของงานก่อนหน้านั่นคือแต่ละเวลาการดำเนินการคือ: initialdelay, initialdelay+ช่วงเวลา, initialdelay+2*ระยะเวลา, ... schedulewithfixeddelay แต่ละครั้ง Initialdelay+ExecuteTime+Delay, Initialdelay+2*ExecuteTime+2*การหน่วงเวลา จะเห็นได้ว่า scheduleatfixedrate ขึ้นอยู่กับช่วงเวลาที่กำหนดสำหรับการกำหนดเวลางานและ ScheduleWithIthFixedDelay ขึ้นอยู่กับความยาวของแต่ละเวลาในการดำเนินงานและขึ้นอยู่กับช่วงเวลาที่ไม่ได้อยู่ในช่วงเวลาสำหรับการกำหนดเวลางาน
ใช้ scheduleDexecutor และปฏิทินเพื่อใช้การจัดตารางงานที่ซับซ้อน
ทั้งตัวจับเวลาและ scheduleDexecutor สามารถให้การจัดตารางงานตามเวลาเริ่มต้นและช่วงเวลาการทำซ้ำเท่านั้นและไม่สามารถใช้งานได้สำหรับข้อกำหนดการกำหนดเวลาที่ซับซ้อนมากขึ้น ตัวอย่างเช่นตั้งค่างานที่จะดำเนินการทุกวันอังคารเวลา 16:38:10 ฟังก์ชั่นนี้ไม่สามารถใช้งานได้โดยตรงโดยใช้ตัวจับเวลาหรือกำหนดเวลา ExtureDExecutor แต่เราสามารถใช้งานได้ทางอ้อมด้วยความช่วยเหลือของปฏิทิน
แพ็คเกจ com.ibm.scheduler; นำเข้า java.util.calendar; นำเข้า java.util.date; นำเข้า java.util.timertask; นำเข้า java.util.concurrent.executors; นำเข้า Java.util.concurrent.scheduledexecutorservice; ScheduleDexceUtortest2 ขยาย TimerTask {สตริงส่วนตัว jobName = ""; Public ScheduleDexceUtortest2 (string jobName) {super (); this.jobname = JobName; } @Override โมฆะสาธารณะเรียกใช้ () {system.out.println ("วันที่ ="+วันที่ใหม่ ()+", ดำเนินการ"+JobName); } /** * คำนวณเวลาล่าสุดที่เริ่มต้นจากเวลาปัจจุบันปัจจุบันและตรงตามเงื่อนไข dayofweek, hourofday, * นาทีที่สอง, secondofminite * @return * /ปฏิทินสาธารณะ getearliestdate (Calendar CurrentDate hour_of_day, นาที, วินาที, ฯลฯ int currentweekofyear = currentDate.get (calendar.week_of_year); int currentdayfweek = 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); // ถ้าวันที่สัปดาห์ในสภาพอินพุตน้อยกว่าวันที่สัปดาห์ของวันที่ปัจจุบันดังนั้นสัปดาห์ที่ผ่านมาจะต้องเลื่อนออกไปโดยหนึ่งสัปดาห์บูลีน Weeklater = False; ถ้า (dayofweek <currentdayofweek) {weeklater = true; } อื่นถ้า (dayofweek == currentdayofweek) {// เมื่อเงื่อนไขการป้อนข้อมูลเท่ากับวันที่สัปดาห์ของวันที่ปัจจุบันถ้า // hourofday ในสภาพอินพุตน้อยกว่า // ปัจจุบันของวันที่ปัจจุบัน {Weeklater = true; } อื่นถ้า (hourofday == currentHour) {// เมื่อเงื่อนไขการป้อนข้อมูลเท่ากับวันที่สัปดาห์วันที่ของวันที่ปัจจุบัน // ถ้านาทีที่อยู่ในสภาพอินพุตน้อยกว่า // ปัจจุบันของวันที่ปัจจุบัน {WeekLater } อื่นถ้า (minuteofhour == currentsecond) {// เมื่อเงื่อนไขอินพุตเท่ากับวันที่สัปดาห์, hourofday, // minuteofhour, ถ้า // secondofminite ในสภาพอินพุตน้อยกว่ากระแสของวันที่ <currentsl) }}}}} ถ้า (Weeklater) {// set week_of_year ในวันที่ปัจจุบันเพื่อเลื่อนหนึ่งสัปดาห์ currentDate.set (Calendar.week_of_year, currentweekofyear + 1); } // set day_of_week, hour_of_day, นาที, วินาทีในวันที่ปัจจุบันเป็นค่าในเงื่อนไขการป้อนข้อมูล currentDate.set (calendar.day_of_week, dayofweek); currentDate.set (calendar.hour_of_day, hourofday); currentDate.SET (Calendar.minute, minuteofhour); currentDate.set (ปฏิทิน. วินาที, secondofminite); คืนกระแสไฟฟ้า; } โมฆะคงที่สาธารณะหลัก (สตริง [] args) โยนข้อยกเว้น {scheduleDexceUtortest2 ทดสอบ = ใหม่ scheduleDexceUtortest2 ("Job1"); // รับปฏิทินเวลาปัจจุบัน CurrentDate = Calendar.getInstance (); Long CurrentDatelong = currentDate.getTime (). getTime (); System.out.println ("วันที่ปัจจุบัน =" + currentDate.getTime (). toString ()); // คำนวณเวลาดำเนินการครั้งสุดท้ายที่ตรงตามปฏิทินเงื่อนไขก่อนหน้านี้ = ทดสอบ. getEarLiestDate (CurrentDate, 3, 16, 38, 10); Long ก่อนหน้านี้ datelong = ournedDate.getTime (). getTime (); System.out.println ("วันที่เร็วที่สุด =" + ก่อนหน้านี้ getTime (). toString ()); // คำนวณช่วงเวลาจากเวลาปัจจุบันไปจนถึงการดำเนินการครั้งสุดท้ายล่าช้าระยะเวลานาน = ก่อนหน้านี้ datelong - CurrentDatelong; // คำนวณระยะเวลาการดำเนินการเป็นระยะเวลานานหนึ่งสัปดาห์ = 7 * 24 * 60 * 60 * 1000; ScheduleDexecutorService Service = Executors.newscheduledThreadPool (10); // จากตอนนี้ล่าช้ามิลลิวินาทีดำเนินการ Job1 service.scheduleatfixedrate (ทดสอบล่าช้าระยะเวลา, timeunit.milliseconds); -/** ผลการส่งออก: วันที่ปัจจุบัน = พุธ 02 172 17:32:01 CST 2011Earliest Date = TUE 8 ก.พ. 16:38:10 CST 2011DATE = TUE 8 ก.พ. 16:38:10 CST 2011 ดำเนินการ JOB1DATE = TUE 15 ก.พ. 16:38:10 CST 2011
รหัสข้างต้นใช้ฟังก์ชั่นของงานกำหนดเวลาเวลา 16:38:10 ทุกวันอังคาร แกนกลางคือการคำนวณเวลาสัมบูรณ์ของ 16:38:10 ในวันอังคารที่ผ่านมาตามเวลาปัจจุบันจากนั้นคำนวณความแตกต่างของเวลาจากเวลาปัจจุบันเป็นพารามิเตอร์ที่เรียกใช้ฟังก์ชัน ScheduleDexceUtor ในการคำนวณเวลาล่าสุดจะใช้ฟังก์ชั่นของ 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
ชุดค่าผสมข้างต้นจะรวมกับ hourofday + นาที + วินาทีเพื่อให้เป็นเครื่องหมายเวลาที่สมบูรณ์
การสาธิตข้างต้นใช้วิธีการรวมกันครั้งสุดท้าย อินพุตคือ day_of_week, hour_of_day, นาที, วินาทีและวันที่ปัจจุบันและผลลัพธ์เป็นวันที่ในอนาคตที่ตอบสนอง day_of_week, hour_of_day, นาที, วินาทีและใกล้กับวันที่ปัจจุบันมากที่สุด หลักการของการคำนวณคือการเริ่มเปรียบเทียบจากอินพุต day_of_week หากน้อยกว่า day_of_week ของวันที่ปัจจุบันคุณจะต้องเพิ่ม Week_of_year ต่อไปนั่นคือเพิ่ม Week_of_year ในวันที่ปัจจุบันและเขียนทับค่าเก่า ถ้ามันเท่ากับ day_of_week ปัจจุบันให้เปรียบเทียบ HOU_OF_DAY ต่อไป; หากมากกว่าวันปัจจุบัน day_of_week โทรไปที่ปฏิทินโดยตรง (ฟิลด์, ค่า) ฟังก์ชั่นของ java.util.calenda เพื่อกำหนด day_of_week, hour_of_day, นาทีที่สองถึงค่าอินพุตและอื่น ๆ จนกว่าจะถึงการเปรียบเทียบ เราสามารถเลือกชุดค่าผสมที่แตกต่างกันตามข้อกำหนดอินพุตเพื่อคำนวณเวลาดำเนินการล่าสุด
ค่อนข้างยุ่งยากในการใช้การกำหนดเวลางานโดยใช้วิธีการข้างต้น เราหวังว่าจะต้องใช้เครื่องมือกำหนดเวลางานที่สมบูรณ์ยิ่งขึ้นเพื่อแก้ปัญหาการกำหนดเวลาที่ซับซ้อนเหล่านี้ โชคดีที่ชุดเครื่องมือโอเพนซอร์สได้แสดงความสามารถที่ยอดเยี่ยมในเรื่องนี้
ควอตซ์
OpenSymphony Open Source Organization เป็นอีกโครงการโอเพนซอร์สในสาขาการจัดตารางงานซึ่งสามารถรวมกับแอปพลิเคชัน J2EE และ J2SE หรือใช้เพียงอย่างเดียว ควอตซ์สามารถใช้ในการสร้างโปรแกรมที่เรียบง่ายหรือซับซ้อนซึ่งทำงานสิบ, หลายร้อยหรือแม้กระทั่งงานนับหมื่น
ลองมาดูตัวอย่าง:
แพ็คเกจ com.test.quartz; นำเข้า org.quartz.datebuilder.newdate; นำเข้า org.quartz.jobbuilder.newjob; นำเข้า org.quartz.simplescheduledulder.simpleschedule; java.util.Gregoriancalendar; นำเข้า org.quartz.jobdetail; นำเข้า org.quartz.scheduler; นำเข้า org.quartz.trigger; นำเข้า org.quartz.impl.stdschedulerfactory; args) {ลอง {// สร้าง scheduler scheduler scheduler = stdschedulerfactory.getDefaultScheduler (); // กำหนดทริกเกอร์ทริกเกอร์ trigger = newTrigger (). withidentity ("trigger1", "group1") // กำหนดชื่อ/กลุ่ม. startNow () // เมื่อเพิ่มตารางเวลาแล้วจะมีผลทันทีด้วยการกำหนดค่า (simpleschedule () มันยังไม่หยุดการสร้าง (); // กำหนด JobDetail JobDetail Job = newJob (helloquartz.class) // กำหนดคลาสงานเป็นคลาส Helloquartz ซึ่งเป็นตรรกะการดำเนินการจริง ("Job1", "Group1") // กำหนดชื่อ/กลุ่ม. UsingJobdata ("ชื่อ" // เพิ่มลงใน scheduler.schedulejob นี้ (งาน, ทริกเกอร์); // เริ่ม Scheduler.start (); // ปิดเธรด SLEEP (10,000); scheduler.shutdown (จริง); } catch (exception e) {e.printstacktrace (); }}} แพ็คเกจ com.test.quartz; นำเข้า java.util.date; นำเข้า org.quartz.disallowconcurrentexecution; นำเข้า org.quartz.job; นำเข้า org.quartz.jobdetail; ดำเนินการ (บริบท jobExecutionContext) พ่น JobExecutionException {JobDetail Detail = context.getJobDetail (); ชื่อสตริง = detail.getJobDatamap (). getString ("ชื่อ"); System.out.println ("Say Hello to" + name + "ที่" + new date ()); -ผ่านตัวอย่างข้างต้น: องค์ประกอบพื้นฐานที่สำคัญที่สุด 3 ประการของควอตซ์:
• Scheduler: Scheduler การกำหนดเวลาทั้งหมดถูกควบคุมโดยมัน
•ทริกเกอร์: กำหนดเงื่อนไขทริกเกอร์ ในตัวอย่างประเภทของมันคือ simpletrigger ซึ่งดำเนินการทุก ๆ 1 วินาที (คือสิ่งที่เป็น simpletrigger จะอธิบายในรายละเอียดด้านล่าง)
• JobDetail & Job: JobDetail กำหนดข้อมูลงานและตรรกะการดำเนินการจริงอยู่ในงานในตัวอย่าง Helloquartz เหตุใดจึงได้รับการออกแบบให้เป็น JobDetail + Job และไม่ใช้งานโดยตรง? นี่เป็นเพราะมันเป็นไปได้ที่จะดำเนินงานพร้อมกัน หาก Scheduler ใช้งานโดยตรงจะมีปัญหาในการเข้าถึงอินสแตนซ์งานเดียวกันพร้อมกัน ในวิธีการ JobDetail & Job ทุกครั้งที่ sheduler ดำเนินการมันจะสร้างอินสแตนซ์งานใหม่ตาม JobDetail เพื่อให้สามารถหลีกเลี่ยงปัญหาการเข้าถึงพร้อมกันได้
API ควอตซ์
สไตล์ API ของควอตซ์คือหลังจาก 2.x และใช้สไตล์ DSL (โดยปกติหมายถึงสไตล์อินเทอร์เฟซที่คล่องแคล่ว) ซึ่งเป็นส่วนใหม่ของ Newtrigger () ในตัวอย่าง มันถูกนำไปใช้ผ่าน Builder ซึ่งเป็นสิ่งต่อไปนี้ (รหัสส่วนใหญ่ต่อไปนี้อ้างถึงผู้สร้างเหล่านี้)
// builderport ที่เกี่ยวข้องกับงาน org.quartz.jobbuilder.*; // builderport ที่เกี่ยวข้องกับทริกเกอร์ org.quartz.triggerbuilder.*; นำเข้า org.quartz.simpleschedulebuilder.*; org.quartz.dailytimeintervalschedulebuilder.*; นำเข้า org.quartz.calendarintervalschedulebuilder static org.calendarintervalschedulebuilder เปรียบเทียบ: JobDetail JobDetail = ใหม่ JobDetailImpl ("JobDetail1", "Group1", helloquartz.class); jobdetail.getjobdatamap (). ใส่ ("ชื่อ", "quartz"); simpletriggerimpl Trigger = new Simpletriggerimpl ( วันที่ ()); trigger.setRepeatInterval (1); trigger.setRepeatCount (-1);เกี่ยวกับชื่อและกลุ่ม
JobDetail และ Trigger มีชื่อและกลุ่ม
ชื่อเป็นตัวระบุที่ไม่ซ้ำกันของพวกเขาใน sheduler นี้ หากเราต้องการอัปเดตคำจำกัดความ JobDetail เราเพียงแค่ต้องตั้งค่าอินสแตนซ์ JobDetail ด้วยชื่อเดียวกัน
Group เป็นหน่วยองค์กรและ Sheduler จะให้ APIs สำหรับกลุ่มปฏิบัติการทั้งหมดเช่น Scheduler.resumeJobs ()
สิ่งกระตุ้น
ก่อนที่จะเริ่มอธิบายรายละเอียดแต่ละทริกเกอร์คุณต้องเข้าใจทริกเกอร์ที่พบบ่อย
starttime & endtime
ช่วงเวลาที่ระบุโดยเริ่มต้นและเวลาสิ้นสุดจะถูกเรียกใช้ นอกช่วงเวลานี้ทริกเกอร์จะไม่ถูกกระตุ้น ทริกเกอร์ทั้งหมดจะมีคุณสมบัติทั้งสองนี้
ลำดับความสำคัญ
เมื่อตัวกำหนดตารางเวลาไม่ว่างจำนวนทริกเกอร์หลายตัวอาจถูกเรียกใช้ในเวลาเดียวกัน แต่ทรัพยากรไม่เพียงพอ (เช่นพูลเธรดไม่เพียงพอ) จากนั้นในเวลานี้วิธีที่ดีกว่าผ้าหินกรรไกรคือการจัดลำดับความสำคัญ ดำเนินการลำดับความสำคัญก่อน ควรสังเกตว่าลำดับความสำคัญจะทำงานระหว่างทริกเกอร์ที่ดำเนินการในเวลาเดียวกันหากทริกเกอร์หนึ่งคือ 9:00 และทริกเกอร์อื่นคือ 9:30 จากนั้นไม่ว่าลำดับความสำคัญถัดไปจะสูงแค่ไหนก่อนหน้านี้จะถูกดำเนินการก่อน ค่าเริ่มต้นของลำดับความสำคัญคือ 5 และค่าเริ่มต้นจะใช้เมื่อเป็นลบ ดูเหมือนว่าค่าสูงสุดจะไม่ได้รับการระบุ แต่ขอแนะนำให้ทำตามมาตรฐาน Java และใช้ 1-10 มิฉะนั้นถ้าคุณรู้ว่ามีค่าที่มากขึ้นเมื่อคุณเห็น [ลำดับความสำคัญคือ 10]
กลยุทธ์ Misfire (Miss Trigger)
เมื่อทรัพยากรตัวกำหนดตารางเวลาที่คล้ายกันไม่เพียงพอหรือเมื่อเครื่องขัดและรีสตาร์ทอาจเป็นไปได้ว่าทริกเกอร์บางตัวจะไม่ถูกกระตุ้นในเวลาที่พวกเขาควรถูกกระตุ้นนั่นคือ Miss Fire ในเวลานี้ Trigger ต้องการกลยุทธ์ในการจัดการกับสถานการณ์นี้ กลยุทธ์เสริมสำหรับแต่ละทริกเกอร์แตกต่างกันไป นี่คือสองจุดที่ให้ความสนใจกับ:
Trigger Misfire มีค่าเกณฑ์ซึ่งกำหนดค่าใน Jobstore มากกว่า ramjobstore คือ org.quartz.jobstore.misfirethreshold Misfire จะถูกนับว่าเกินเกณฑ์นี้เท่านั้น น้อยกว่าเกณฑ์นี้ควอตซ์จะถูก retriggered กลยุทธ์ Misfire ทั้งหมดตอบคำถามสองข้อ:
•นั่นต้องมีการตีผิดอีกครั้งหรือไม่?
•หากเกิดความผิดพลาดคุณต้องการปรับเวลาการกำหนดเวลาที่มีอยู่หรือไม่?
ตัวอย่างเช่นกลยุทธ์ Misfire ของ Simpletrigger รวมถึง:
• misfire_instruction_ignore_misfire_policy นี่ไม่ได้หมายถึงการเพิกเฉยต่อทริกเกอร์ที่ไม่ได้รับ แต่ไม่สนใจนโยบายที่ผิดพลาด มันจะดึงงานที่ผิดพลาดทั้งหมดเมื่อทรัพยากรเหมาะสมและจะไม่ส่งผลกระทบต่อเวลาการกำหนดเวลาที่มีอยู่ ตัวอย่างเช่น Simpletrigger ดำเนินการทุก ๆ 15 วินาทีและใช้เวลา 5 นาทีในช่วงกลางและพลาด 20 ครั้ง หลังจาก 5 นาทีสมมติว่าทรัพยากรเพียงพอและงานอนุญาตให้เกิดขึ้นพร้อมกันมันจะถูกกระตุ้นในครั้งเดียว คุณสมบัตินี้ใช้กับทริกเกอร์ทั้งหมด
• misfire_instruction_fire_now ละเว้นงานที่มีความผิดพลาดและดำเนินการตามกำหนดเวลาทันที โดยปกติจะใช้กับงานที่ดำเนินการเพียงครั้งเดียวเท่านั้น
• misfire_instruction_reschedule_now_with_existing_repeat_count ตั้งค่าเวลาเริ่มต้นเป็นเวลาปัจจุบันและจัดตารางงานทันทีรวมถึง Misfire
• misfire_instruction_reschedule_now_with_remaining_repeat_count คล้ายกับ MisfireInstructionReschedulenowveTexistingRepeat_count ความแตกต่างคืองานที่มีความผิดพลาดไปแล้ว
• misfire_instruction_reschedule_next_with_existing_count ในเวลาที่กำหนดถัดไปรีสตาร์ทงานจัดส่งรวมถึง Misfire
• misfire_instruction_reschedule_next_with_remaining_count คล้ายกับ MisfireinstructionReschedulenextWitteKexistingCount ความแตกต่างคืองานที่มีความผิดพลาดไปแล้ว
• misfire_instruction_smart_policy ค่าเริ่มต้นของทริกเกอร์ทั้งหมดคือสิ่งนี้ซึ่งหมายถึง "ปล่อยตรรกะการประมวลผลไปยังสมาร์ทควอตซ์เพื่อตัดสินใจ" กลยุทธ์พื้นฐานคือ
•หากกำหนดการถูกดำเนินการเพียงครั้งเดียวให้ใช้ Misfire_Instruction_fire_Now
•หากเป็นการกำหนดเวลาที่ไม่มีที่สิ้นสุด
•มิฉะนั้นการใช้ misfire_instruction_reschedule_now_with_existing_repeat_count misfire ค่อนข้างซับซ้อนคุณสามารถอ้างถึงบทความนี้
ปฏิทิน
ปฏิทินที่นี่ไม่ใช่ java.util.calendar ของ JDK ไม่ใช่สำหรับการคำนวณวันที่ ฟังก์ชั่นของมันคือการเสริมเวลาของทริกเกอร์ จุดเฉพาะบางอย่างในเวลาสามารถยกเว้นหรือเพิ่ม
รับ "การชำระหนี้การ์ดอัตโนมัติเวลา 0:00 น. ในวันที่ 25 ของแต่ละเดือน" เป็นตัวอย่างเราต้องการแยกแยะช่วงเวลาของวันที่ 25 กุมภาพันธ์ของทุกปี (เนื่องจากมี 2.14, กุมภาพันธ์จะล้มละลายอย่างแน่นอน) เวลานี้สามารถทำได้โดยปฏิทิน
ตัวอย่าง:
ประจำปี calendar cal = new ประจำปี calendar (); // กำหนดปฏิทินที่ดำเนินการทุกปีด้วยความแม่นยำของวันนั่นคือมันไม่สามารถกำหนดได้จนถึง 14:00 น. ในเวลา 2.25 java.util.calendar excludeday = New Gregoriancalendar (); // กำหนดวันที่ยกเว้น 2.25 Scheduler.addCalendar ("Febcal", Cal, False, False); // Scheduler เข้าร่วมปฏิทินนี้ // กำหนด triggerTrigger trigger = newTrigger (). Withidentity ("Trigger1", "Group1"). StartNow () // เมื่อเพิ่มตารางเวลาจะมีผลทันที .withschedule (simpleschedule (). withintervalinseconds (1) .repeatforever ()) .build ();ควอตซ์ให้ปฏิทินต่อไปนี้แก่เรา โปรดทราบว่าปฏิทินทั้งหมดสามารถยกเว้นหรือรวมขึ้นอยู่กับ:
• HolidayCalendar ระบุวันที่เฉพาะเช่น 20140613 ความแม่นยำสู่ท้องฟ้า
• DailyCalendar ระบุช่วงเวลาของแต่ละวัน (RangestartingTime, ช่วงเวลา), รูปแบบคือ HH: MM [: SS [: MMMM]] นั่นคือความแม่นยำสูงสุดสามารถเข้าถึงมิลลิวินาที
• WeeklyCalendar ระบุวันของสัปดาห์ของแต่ละสัปดาห์ค่าเสริมคือ java.util.calendar.sunday ความแม่นยำคือวัน
• MonthlyCalendar ระบุวันของแต่ละเดือน ค่าเสริมคือ 1-31 ความแม่นยำคือวัน
• Calendar ประจำปี ระบุวันใดของปี วิธีการใช้งานดังแสดงในตัวอย่างด้านบน ความแม่นยำคือวัน
• Croncalendar ระบุการแสดงออกของ cron ความแม่นยำขึ้นอยู่กับการแสดงออกของ cron นั่นคือความแม่นยำสูงสุดสามารถเข้าถึงไม่กี่วินาที
คลาสการใช้งานทริกเกอร์
ควอตซ์มีการใช้ทริกเกอร์ต่อไปนี้:
Simpletrigger
ระบุงานที่เริ่มต้นในช่วงเวลาหนึ่งและดำเนินการในช่วงเวลาที่กำหนด (เป็นมิลลิวินาที) เหมาะสำหรับงานที่คล้ายกับ: เริ่มต้นที่ 9:00 และดำเนินการทุก ๆ 1 ชั่วโมง คุณสมบัติของมันคือ:
•ช่วงเวลาซ้ำซ้ำ
•จำนวนเงินซ้ำของการทำซ้ำ จำนวนการประหารชีวิตจริงคือการนับซ้ำ+1 เพราะมันจะถูกดำเนินการหนึ่งครั้งเมื่อเริ่มต้น เช่นเดียวกับคุณสมบัติ repeatcount ด้านล่าง
ตัวอย่าง:
ปฏิทิน
เช่นเดียวกับ Simpletrigger มันระบุงานที่เริ่มต้นในเวลาที่กำหนดและดำเนินการในช่วงเวลาที่กำหนด แต่ความแตกต่างคือช่วงเวลาที่ระบุโดย Simpletrigger คือมิลลิวินาทีและไม่มีวิธีระบุว่าจะดำเนินการทุกเดือน (ช่วงเวลารายเดือนไม่ใช่ค่าคงที่) ในขณะที่หน่วยช่วงเวลาที่ได้รับการสนับสนุนโดย Calendarintervaltrigger รวมวินาทีนาทีชั่วโมงวันเดือนเดือนและสัปดาห์ เมื่อเทียบกับ Simpletrigger มีข้อดีสองประการ: 1. สะดวกกว่า ตัวอย่างเช่นหากคุณดำเนินการทุก ๆ 1 ชั่วโมงคุณไม่จำเป็นต้องคำนวณจำนวนมิลลิวินาที 1 ชั่วโมงเท่ากับ 2. สนับสนุนช่วงเวลาที่ไม่ได้มีความยาวคงที่เช่นช่วงเวลาที่เป็นเดือนและปี แต่ข้อเสียคือความแม่นยำสามารถเข้าถึงได้เพียงไม่กี่วินาที งานที่เหมาะสมนั้นคล้ายกับ: เริ่มต้นที่ 9:00 และดำเนินการสัปดาห์ละครั้งทุกสัปดาห์เวลา 9:00 น. คุณสมบัติของมันคือ:
•ช่วงเวลาการดำเนินการตามช่วงเวลา
•หน่วยช่วงเวลาการดำเนินการ (วินาที, นาที, ชั่วโมง, วัน, เดือน, ปี, สัปดาห์)
ตัวอย่าง:
CalendarIntervalschedule () .WithIntervalindays (1) // ดำเนินการวันละครั้งวัน. build (); CalendarIntervalschedule (). withintervalinweeks (1) // ดำเนินการสัปดาห์ละครั้ง build ();
DailyTimeIntervaltrigger
ระบุว่างานจะดำเนินการในบางช่วงเวลาในช่วงระยะเวลาหนึ่งทุกวัน และสามารถรองรับสัปดาห์ที่ระบุ งานที่เหมาะสมนั้นคล้ายกับ: ระบุ 9:00 ถึง 18:00 น. ทุกวันดำเนินการทุก ๆ 70 วินาทีและมีเพียงวันจันทร์ถึงวันศุกร์ คุณสมบัติของมันคือ:
•เริ่มต้นวันเริ่มต้นวันใหม่ทุกวัน
• endtimeofday end time ของวัน
• Daysofweek สัปดาห์ที่จะดำเนินการ
•ช่วงเวลาการดำเนินการตามช่วงเวลา
•หน่วยช่วงเวลาการดำเนินการ (วินาที, นาที, ชั่วโมง, วัน, เดือน, ปี, สัปดาห์)
•จำนวนเงินซ้ำของการทำซ้ำ
ตัวอย่าง:
DailyTimeIntervalschedule (). startingDailyat (timeofday.hourandminuteofday (9, 0)) // เริ่มเวลา 9:00 น. ในวันที่วันหยุด (timeofday.hourandminuteofday (16, 0)) // สิ้นสุดที่ 16:00 น. ภายในเทอร์วัลลิน (1) // ดำเนินการทุก ๆ 1 ชั่วโมง WithRepeatCount (100) // ทำซ้ำสูงสุด 100 ครั้ง (จริง ๆ แล้วดำเนินการจริง 100+1 ครั้ง). build (); DailyTimeIntervalschedule (). startingDailyat (timeofday.HourandMinuteOfday (9, 0)) // เริ่มต้นที่ 9:00 วิธีนี้จริง ๆ แล้วคำนวณ endtimeofday ตาม starttimeofday+interval*นับ. ondaysoftheweek (วันจันทร์, วันอังคาร, วันพุธ, วันพฤหัสบดี, วันศุกร์) // ดำเนินการตั้งแต่วันจันทร์ถึงวันศุกร์ withintervalinhours (1) // ดำเนินการทุก ๆ 1 ชั่วโมง
crontrigger
เหมาะสำหรับงานที่ซับซ้อนมากขึ้นรองรับไวยากรณ์ที่พิมพ์ไปยัง Linux cron (และมีประสิทธิภาพมากกว่า) โดยพื้นฐานแล้วมันครอบคลุมส่วนใหญ่ (แต่ไม่ใช่ทั้งหมด) ของทริกเกอร์สามข้างต้น - แน่นอนมันก็ยากที่จะเข้าใจ งานที่เหมาะสมของมันคล้ายกับ: แต่ละครั้งแสดงทุกวันเวลา 0:00, 9:00 และ 18:00 น. คุณสมบัติของมันเป็นเพียง:
การแสดงออกของ cron
แต่การเป็นตัวแทนนี้มีความซับซ้อนเพียงพอ จะมีคำแนะนำด้านล่าง ตัวอย่าง:
cronschedule ("0 0/2 8-17 * *?") // ดำเนินการทุก 2 นาทีเวลา 8: 00-17: 00 ทุกวัน build (); cronschedule ("0 30 9? * mon") // ดำเนินการทุกวันจันทร์เวลา 9: 30 * mon .build ();การแสดงออกของ cron
| ที่ตั้ง | โดเมนเวลา | ค่าที่อนุญาต | ค่าพิเศษ |
| 1 | ที่สอง | 0-59 | - |
| 2 | นาที | 0-59 | - |
| 3 | ชั่วโมง | 0-23 | - |
| 4 | วันที่ | 1-31 | - / LWC |
| 5 | เดือน | 1-12 | - |
| 6 | สัปดาห์ | 1-7 | - / lc # |
| 7 | ปี (ไม่บังคับ) | 1-31 | - |
• Asterisk (): สามารถใช้ในทุกฟิลด์เพื่อแสดงแต่ละช่วงเวลาในโดเมนเวลาที่สอดคล้องกันเช่นในฟิลด์นาทีมันหมายถึง "ต่อนาที";
•เครื่องหมายคำถาม (?): ตัวละครนี้ใช้เฉพาะในฟิลด์วันที่และสัปดาห์เท่านั้นและมักจะระบุว่าเป็น "ค่าที่ไม่มีความหมาย" เทียบเท่ากับตัวละคร DOT;
•เครื่องหมายลบ (-): แสดงช่วง หากใช้ "10-12" ในสนามชั่วโมงมันหมายถึง 10 ถึง 12 คะแนนนั่นคือ 10,11,12;
•เครื่องหมายจุลภาค (,): แสดงค่ารายการ ถ้า "วันจันทร์, พุธ, ศุกร์" ถูกใช้ในสนามสัปดาห์ก็หมายถึงวันจันทร์วันพุธและวันศุกร์
• 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 สุดท้ายของเดือน" ตัวอย่างเช่น 6L หมายถึงวันศุกร์สุดท้ายของเดือน
• W: อักขระนี้สามารถปรากฏในฟิลด์วันที่เท่านั้นและเป็นการปรับเปลี่ยนวันที่นำซึ่งแสดงถึงวันทำงานที่ใกล้เคียงที่สุดกับวันที่ ตัวอย่างเช่น 15W แสดงถึงวันทำงานที่ใกล้เคียงที่สุดถึงวันที่ 15 ของเดือน หากวันที่ 15 ของเดือนคือวันเสาร์มันจะตรงกับวันศุกร์ที่ 14; หากวันที่ 15 ของเดือนคือวันอาทิตย์มันจะตรงกับวันจันทร์ที่ 16; หากวันที่ 15 ของเดือนคือวันอังคารมันเป็นวันอังคารที่ 15 อย่างไรก็ตามจะต้องสังเกตว่าวันที่จับคู่ที่เกี่ยวข้องไม่สามารถข้ามไปยังเดือน หากคุณระบุ 1W หากวันที่ 1 คือวันเสาร์ผลลัพธ์จะตรงกับวันจันทร์ที่ 3 ไม่ใช่วันสุดท้ายของเดือนที่แล้ว สตริง W สามารถระบุวันที่เดียวเท่านั้น แต่ไม่สามารถระบุช่วงวันที่ได้
•การรวมกันของ LW: LW สามารถใช้ในฟิลด์วันที่ซึ่งหมายถึงวันทำการสุดท้ายของเดือน Pound Sign (#): ตัวละครนี้สามารถใช้งานได้ในฟิลด์สัปดาห์ซึ่งเป็นวันทำงานของเดือน ตัวอย่างเช่น 6#3 หมายถึงวันศุกร์ที่สามของเดือน (6 หมายถึงวันศุกร์#3 หมายถึงวันที่สามในขณะนี้) ในขณะที่ 4#5 หมายถึงวันพุธที่ห้าของเดือนโดยสมมติว่าเดือนไม่มีวันพุธที่ห้า
• C: ตัวละครนี้ใช้เฉพาะในเขตข้อมูลวันที่และสัปดาห์ซึ่งแสดงถึงความหมายของ "ปฏิทิน" มันหมายถึงวันที่ที่เกี่ยวข้องกับแผนและหากวันที่ไม่เกี่ยวข้องจะเทียบเท่ากับวันที่ทั้งหมดในปฏิทิน ตัวอย่างเช่น 5c ในฟิลด์วันที่เทียบเท่ากับวันแรกหลังจากวันที่ 5 ของปฏิทิน 1C เทียบเท่ากับวันแรกหลังจากวันอาทิตย์ในสนามสัปดาห์
การแสดงออกของ cron ไม่ไวต่อกรณีของตัวละครพิเศษและไม่ไวต่อตัวย่อของคดีภาษาอังกฤษของสัปดาห์ ตัวอย่างบางส่วน:
| การแสดงออก | อธิบาย |
| 0 0 12 * *? | วิ่งเวลา 12 นาฬิกาทุกวัน |
| 0 15 10? - | วิ่งเวลา 10:15 ทุกวัน |
| 0 15 10 * *? | วิ่งเวลา 10:15 ทุกวัน |
| 0 15 10 * *? - | วิ่งเวลา 10:15 ทุกวัน |
| 0 15 10 * *? 2551 | ดำเนินการเวลา 10:15 วันในปี 2551 |
| 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 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次,那么就有可能多个任务被并发执行。
有时候我们并不想任务并发执行,比如这个任务要去”获得数据库中所有未发送邮件的名单“,如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。这个时候一个@DisallowConcurrentExecution解决这个问题。就是这样:
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表达式的格式:秒分时日月周年(可选)。
Field name | Allowed value | Allowed special characters ------- | - - -
字符含义:
•*:代表所有可能的值。因此,“*”在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 มากขึ้น