หากเราต้องการดำเนินการตามกำหนดเวลาง่าย ๆ ระหว่างกระบวนการเขียนโปรแกรมเราไม่จำเป็นต้องควบคุมที่ซับซ้อน เราสามารถพิจารณาใช้งานการจับเวลาตัวจับเวลาใน JDK เพื่อให้ได้มัน LZ ต่อไปนี้วิเคราะห์ตัวจับเวลา Java ตามหลักการตัวอย่างและข้อบกพร่องของตัวจับเวลา
1. บทนำ
ใน Java งานเวลาที่สมบูรณ์จะต้องเสร็จสิ้นโดยคลาส Timer และ Timertask นี่คือวิธีที่พวกเขาถูกกำหนดไว้ใน API ตัวจับเวลา: เครื่องมือที่ใช้เพื่อจัดเรียงงานที่เธรดดำเนินการในเธรดพื้นหลังในภายหลัง งานสามารถดำเนินการได้หนึ่งครั้งหรือสามารถดำเนินการซ้ำ ๆ ได้ งานที่กำหนดโดย Timertask: จับเวลาเป็นงานที่ดำเนินการหรือทำซ้ำ เราสามารถเข้าใจได้ว่าตัวจับเวลาเป็นเครื่องมือจับเวลาที่ใช้ในการวางแผนที่จะดำเนินการงานที่ระบุในเธรดพื้นหลังและ Timertask คลาสนามธรรมที่มีคลาสย่อยแสดงถึงงานที่สามารถวางแผนได้โดยตัวจับเวลา
ตัวจับเวลาคลาส <br /> ในตัวจับเวลาคลาสเครื่องมือมีวิธีการสร้างสี่วิธี แต่ละคอนสตรัคเตอร์เริ่มเธรดจับเวลา ในเวลาเดียวกันคลาสจับเวลาสามารถตรวจสอบให้แน่ใจว่าหลายเธรดสามารถแชร์วัตถุตัวจับเวลาเดียวโดยไม่ต้องซิงโครไนซ์ภายนอกดังนั้นคลาสจับเวลาจึงปลอดภัย อย่างไรก็ตามเนื่องจากวัตถุตัวจับเวลาแต่ละรายการสอดคล้องกับเธรดพื้นหลังเดียวซึ่งใช้ในการเรียกใช้งานตัวจับเวลาทั้งหมดตามลำดับโดยทั่วไปเวลาที่ใช้ในการดำเนินงานเธรดของเราควรสั้นมาก อย่างไรก็ตามเนื่องจากสถานการณ์พิเศษเวลาดำเนินการของงานตัวจับเวลาบางอย่างนั้นยาวเกินไปดังนั้นจึง "เฉพาะ" เธรดการดำเนินการจับเวลาของตัวจับเวลาและเธรดที่ตามมาทั้งหมดจะต้องรอให้ดำเนินการซึ่งจะชะลอการดำเนินงานที่ตามมา เราจะวิเคราะห์สถานการณ์เฉพาะในภายหลัง
เมื่อโปรแกรมเริ่มต้นตัวจับเวลางานเวลาจะถูกดำเนินการตามเวลาที่เราตั้งไว้ ตัวจับเวลาจัดเตรียมวิธีการกำหนดเวลาซึ่งมีการโอเวอร์โหลดหลายครั้งเพื่อปรับให้เข้ากับสถานการณ์ที่แตกต่างกันดังนี้:
กำหนดเวลา (งาน TimerTask เวลาวันที่): กำหนดเวลาการดำเนินการของงานที่ระบุในเวลาที่กำหนด
กำหนดเวลา (งาน TimerTask, วันที่ครั้งแรก, ระยะเวลานาน): กำหนดเวลางานที่ระบุเพื่อเริ่มต้นการดำเนินการล่าช้าแก้ไขซ้ำซ้ำในเวลาที่กำหนด
กำหนดเวลา (งาน TimerTask, การหน่วงเวลานาน): กำหนดเวลางานที่ระบุที่จะดำเนินการหลังจากการหน่วงเวลาที่ระบุ
กำหนดเวลา (งาน TimerTask, การหน่วงเวลานาน, เป็นเวลานาน): กำหนดเวลางานที่ระบุที่จะดำเนินการแก้ไขล่าช้าซ้ำ ๆ หลังจากการหน่วงเวลาที่ระบุ
ในเวลาเดียวกันวิธีการกำหนดตารางเวลา FIXEDRATE ก็เกินพิกัดเช่นกัน วิธี ScheduleatFixedrate นั้นเหมือนกับกำหนดการ แต่โฟกัสของพวกเขาแตกต่างกันและความแตกต่างจะถูกวิเคราะห์ในภายหลัง
ScheduleatFixedrate (งาน TimerTask, วันที่ครั้งแรก, ระยะเวลานาน): กำหนดเวลางานที่ระบุที่จะดำเนินการซ้ำ ๆ ในอัตราคงที่ในเวลาที่กำหนด
ScheduleatFixedrate (งาน TimerTask, การหน่วงเวลานาน, เป็นเวลานาน): กำหนดเวลางานที่ระบุเพื่อเริ่มต้นการดำเนินการอัตราคงที่ซ้ำ ๆ หลังจากการหน่วงเวลาที่ระบุ
timertask
คลาส Timertask เป็นคลาสนามธรรมที่จัดโดยตัวจับเวลาเป็นงานที่ดำเนินการหรือทำซ้ำ มันมีวิธีนามธรรม Run () ซึ่งใช้ในการดำเนินการที่จะดำเนินการโดยงานจับเวลาที่สอดคล้องกัน ดังนั้นแต่ละคลาสงานเฉพาะจะต้องสืบทอด timertask จากนั้นแทนที่วิธีการเรียกใช้ ()
นอกจากนี้ยังมีสองวิธีที่ไม่ได้รับการรับรอง:
บูลีนยกเลิก (): ยกเลิกงานจับเวลานี้
Long ScheduleDexecutionTime (): ส่งคืนเวลาดำเนินการตามกำหนดเวลาของการดำเนินการจริงล่าสุดของงานนี้
2. ตัวอย่าง
2.1. ระบุเวลาหน่วงในการดำเนินการตามกำหนดเวลา
ระดับสาธารณะ TimerTest01 {ตัวจับเวลาตัวจับเวลา; timertest01 สาธารณะ (เวลา int) {timer = new timer (); timer.schedule (TimertaskTest01 () ใหม่เวลา * 1000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("ตัวจับเวลาเริ่ม ... "); ใหม่ TimerTest01 (3); }} คลาสสาธารณะ TimerTaskTest01 ขยาย TimerTask {โมฆะสาธารณะ Run () {System.out.println ("Time's Up !!!"); - ผลการทำงาน:
พิมพ์ครั้งแรก: ตัวจับเวลาเริ่ม ...
พิมพ์ใน 3 วินาที: เวลาขึ้น !!!
2.2. ดำเนินงานเวลาตามเวลาที่กำหนด
ระดับสาธารณะ TimerTest02 {ตัวจับเวลาตัวจับเวลา; timertest02 สาธารณะ () {เวลาวันที่ = getTime (); System.out.println ("ระบุเวลา =" + เวลา); ตัวจับเวลา = ตัวจับเวลาใหม่ (); timer.schedule (TimertaskTest02 (), เวลา); } วันที่สาธารณะ getTime () {ปฏิทินปฏิทิน = calendar.getInstance (); Calendar.set (Calendar.hour_of_day, 11); Calendar.set (Calendar.minute, 39); Calendar.set (Calendar.second, 00); เวลาวันที่ = calendar.getTime (); เวลากลับ; } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ใหม่ timertest02 (); }} คลาสสาธารณะ TimerTaskTest02 ขยาย TimerTask {@Override โมฆะสาธารณะเรียกใช้ () {System.out.println ("ดำเนินการงานเธรดตามเวลาที่กำหนด ... "); - เมื่อถึงเวลา 11:39:00 น. งานเธรดจะถูกดำเนินการแน่นอนมันจะถูกดำเนินการแม้ว่ามันจะยิ่งใหญ่กว่าเวลานั้น! - ผลการดำเนินการคือ:
เวลาที่ระบุ = อังคาร 10 มิ.ย. 11:39:00 CST 2014
ดำเนินงานเธรดตามเวลาที่กำหนด ...
2.3. หลังจากล่าช้าเวลาที่กำหนดงานเวลาจะถูกดำเนินการตามเวลาที่กำหนด
ระดับสาธารณะ TimerTest03 {ตัวจับเวลาตัวจับเวลา; timertest03 () {timer = new timer (); Timer.Schedule (ใหม่ TimerTaskTest03 (), 1,000, 2000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ใหม่ timertest03 (); }} คลาสสาธารณะ TimerTaskTest03 ขยาย TimerTask {@Override โมฆะสาธารณะเรียกใช้ () {วันที่ = วันที่ใหม่ (this.ScheduleDexecutiontime ()); System.out.println ("เวลาสำหรับการดำเนินการเธรดนี้คือ:" + วันที่); - ผลการทำงาน:
เวลาในการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:47 CST 2014
เวลาสำหรับการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:49 CST 2014
เวลาสำหรับการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:51 CST 2014
เวลาสำหรับการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:53 CST 2014
เวลาสำหรับการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:55 CST 2014
เวลาสำหรับการดำเนินการเธรดนี้คือ: อังคาร 10 มิ.ย. 21:19:57 CST 2014
-
สำหรับงานเธรดนี้หากเราไม่หยุดงานมันจะทำงานต่อไป
สำหรับสามตัวอย่างข้างต้น LZ เพิ่งแสดงให้เห็นสั้น ๆ และไม่ได้อธิบายตัวอย่างของวิธี ScheduleatFixedrate ในความเป็นจริงวิธีนี้เหมือนกับวิธีกำหนดเวลา!
2.4. วิเคราะห์กำหนดการและกำหนดการ FIXEDRATE
1), กำหนดการ (งาน TimerTask, เวลาวันที่), กำหนดเวลา (งาน TimerTask, ล่าช้านาน)
สำหรับทั้งสองวิธีหากกำหนดการกำหนดเวลาที่ระบุ <= SystemCurrentTime งานจะถูกดำเนินการทันที ScheduleDexecutiontime จะไม่เปลี่ยนแปลงเนื่องจากการดำเนินงานมากเกินไป
2) กำหนดเวลา (งาน TimerTask วันที่ครั้งแรกระยะเวลานาน) กำหนดเวลา (งาน TimerTask ล่าช้าระยะยาวระยะยาว)
สองวิธีนี้แตกต่างจากสองวิธีข้างต้น ดังที่ได้กล่าวไว้ก่อนหน้านี้งานจับเวลาตัวจับเวลาจะล่าช้าเนื่องจากงานก่อนหน้านี้ถูกดำเนินการเป็นเวลานาน ในสองวิธีนี้เวลาที่กำหนดเวลาของงานแต่ละงานที่ดำเนินการจะเปลี่ยนไปตามเวลาจริงของงานก่อนหน้านั่นคือ scheduleDexecutiontime (n+1) = realexecutiontime (n)+ช่วงเวลา กล่าวคือหากงาน nth ทำให้กระบวนการเวลาดำเนินการนี้เกิดขึ้นเนื่องจากสถานการณ์บางอย่างและในที่สุด SystemCurrentTime> = ScheduleDexecutiontime (N+1) นี่คืองาน N+1 และจะไม่ถูกดำเนินการเนื่องจากเวลา มันจะรอให้งาน nth ดำเนินการก่อนการดำเนินการจากนั้นสิ่งนี้จะนำไปสู่การใช้งาน N+2 การดำเนินการตามกำหนดเวลาและการเปลี่ยนแปลงของการดำเนินการตามกำหนดเวลาและการเปลี่ยนแปลงนั่นคือ scheduleDexecutiontime (n+2) = realexecutiontime (N+1)+ช่วงเวลา ดังนั้นวิธีการทั้งสองนี้ให้ความสำคัญกับความเสถียรของช่วงเวลาการจัดเก็บมากขึ้น
3) ScheduleatFixedrate (งาน TimerTask, วันที่ครั้งแรก, ระยะเวลานาน), scheduleatfixedrate (งาน TimerTask, การหน่วงเวลานาน, ระยะเวลานาน)
ดังที่ได้กล่าวไว้ก่อนหน้านี้การมุ่งเน้นของ ScheduleatFixedrate และวิธีการกำหนดเวลานั้นแตกต่างกัน วิธีการกำหนดเวลามุ่งเน้นไปที่ความเสถียรของช่วงเวลาการออมในขณะที่วิธีการกำหนดตารางเวลา Fixedrate มุ่งเน้นไปที่การรักษาเสถียรภาพของความถี่ในการดำเนินการ ทำไมคุณถึงพูดอย่างนั้นเหตุผลมีดังนี้ ในวิธีการกำหนดเวลาความล่าช้าของงานก่อนหน้านี้จะทำให้เกิดความล่าช้าของงานเวลาหลังจากนั้นในขณะที่วิธีการกำหนดตารางเวลา fixedrate จะไม่ หากเวลาการดำเนินการของงาน nth ยาวเกินไป SystemCurrentTime> = scheduleDexecutiontime (n+1) จะไม่มีการรอให้มันดำเนินการงาน n+1 ทันที ดังนั้นวิธีการคำนวณของเวลาดำเนินการของวิธี ScheduleatFixedrate นั้นแตกต่างจากกำหนดเวลา แต่กำหนดเวลา Executiontime (n) = FirstExecuteTime +N*ระยะเวลาและวิธีการคำนวณจะยังคงไม่เปลี่ยนแปลงตลอดไป ดังนั้น ScheduleatFixedrate จึงมุ่งเน้นไปที่การรักษาความถี่การดำเนินการให้เสถียร
3. ข้อบกพร่องของตัวจับเวลา
3.1. ข้อบกพร่องของตัวจับเวลา
ตัวจับเวลาตัวจับเวลาสามารถเวลา (ดำเนินการงานตามเวลาที่กำหนด), ล่าช้า (งานล่าช้าที่ 5 วินาที) และดำเนินการเป็นระยะ (ดำเนินการงานที่ 1 วินาที) แต่ตัวจับเวลามีข้อบกพร่องบางอย่าง ก่อนอื่นการสนับสนุนของ Timer สำหรับการจัดตารางเวลานั้นขึ้นอยู่กับเวลาที่แน่นอนไม่ใช่เวลาที่สัมพันธ์กันดังนั้นจึงมีความอ่อนไหวต่อการเปลี่ยนแปลงเวลาของระบบ ประการที่สองเธรดตัวจับเวลาจะไม่จับข้อยกเว้น หากข้อยกเว้นที่ไม่ได้ตรวจสอบถูกโยนโดย Timertask มันจะทำให้เธรดจับเวลาสิ้นสุดลง ในเวลาเดียวกันตัวจับเวลาจะไม่ดำเนินการต่อการดำเนินการของเธรดและจะเชื่อผิดพลาดว่าเธรดตัวจับเวลาทั้งหมดจะถูกยกเลิก ในเวลาเดียวกัน Timertask ซึ่งได้รับการกำหนดให้ยังไม่ได้ดำเนินการจะไม่ถูกดำเนินการอีกต่อไปและไม่สามารถกำหนดงานใหม่ได้ ดังนั้นหาก Timertask โยนข้อยกเว้นที่ไม่ได้ตรวจสอบตัวจับเวลาจะสร้างพฤติกรรมที่คาดเดาไม่ได้
1) การจัดการเวลาการจัดการเวลาหน่วงเวลาข้อบกพร่อง <br /> ตัวจับเวลาก่อนหน้านี้จะสร้างงานเธรดหนึ่งรายการเมื่อดำเนินงานที่กำหนดเวลา หากมีหลายเธรดถ้าหนึ่งในเธรดทำให้เวลาในการดำเนินงานของเธรดยาวเกินไปด้วยเหตุผลบางอย่างและช่วงเวลาระหว่างงานทั้งสองเกินเวลาระหว่างพวกเขาข้อบกพร่องบางอย่างจะเกิดขึ้น:
TimerTests04 ชั้นเรียนสาธารณะ {ตัวจับเวลาตัวจับเวลาส่วนตัว; การเริ่มต้นที่ยาวนานของสาธารณะ TimerTest04 () {this.timer = new timer (); start = system.currentTimeMillis (); } โมฆะสาธารณะ timerone () {timer.schedule (timertask ใหม่ () {public void run () {system.out.println ("timerone เรียกใช้เวลา:" + (system.currentTimeMillis () - เริ่มต้น); ลอง {thread.sleep (4000); E.PrintStackTrace ();}}}}, 1000); } โมฆะสาธารณะ timertwo () {timer.schedule (ใหม่ timertask () {public void run () {system.out.println ("timerone เรียกใช้เวลา:" + (system.currenttimeMillis () - เริ่มต้น));}}, 3000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่นข้อยกเว้น {timerTest04 ทดสอบ = ใหม่ timerTest04 (); test.timerone (); test.timertwo (); - ตามความคิดปกติของเรา Timertwo ควรดำเนินการหลังจาก 3s และผลลัพธ์ควรเป็น:
Timerone เรียกใช้เวลา: 1001
Timerone เรียกใช้เวลา: 3001
แต่สิ่งต่าง ๆ ขัดกับความคาดหวังของฉัน Timerone นอนหลับ (4000) นอน 4S และตัวจับเวลาอยู่ในตัวจับเวลาซึ่งเป็นสาเหตุของเวลาที่ต้องใช้เวลาในการกำหนดเวลาเกินช่วงเวลา ผลลัพธ์:
Timerone เรียกใช้เวลา: 1,000
Timerone เรียกใช้เวลา: 5000
2) ตัวจับเวลาโยนข้อบกพร่องข้อยกเว้น
หาก Timertask โยน runtimeException ตัวจับเวลาจะยุติงานทั้งหมด ดังนี้:
TimerTests04 ชั้นเรียนสาธารณะ {ตัวจับเวลาตัวจับเวลาส่วนตัว; TimerTest04 () {this.timer = new timer (); } โมฆะสาธารณะ timerone () {timer.schedule (ใหม่ timertask () {public void run () {โยน runtimeException ใหม่ ();}}, 1000); } โมฆะสาธารณะ timertwo () {timer.schedule (ใหม่ timertask () {public void run () {system.out.println ("ฉันจะดำเนินการหรือไม่?");}}, 1,000); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {timerTest04 ทดสอบ = ใหม่ timerTest04 (); test.timerone (); test.timertwo (); - ผลการทำงาน: Timerone โยนข้อยกเว้นทำให้งาน Timertwo สิ้นสุดลง
ข้อยกเว้นในเธรด "Timer-0" java.lang.runtimeException ที่ com.chenssy.timer.timertest04 $ 1.Run (TimerTest04.Java:25) ที่ java.util.timerthread.mainloop (timer.java:555)
สำหรับข้อบกพร่องของตัวจับเวลาเราสามารถพิจารณา ScheduledThreadPoolexecutor แทน ตัวจับเวลาขึ้นอยู่กับเวลาที่แน่นอนและมีความอ่อนไหวต่อเวลาของระบบมากขึ้นในขณะที่ scheduledThreadPoolexecutor ขึ้นอยู่กับเวลาที่สัมพันธ์กัน ตัวจับเวลาเป็นเธรดเดียวภายในในขณะที่ ScheduleDThreadPoolexecutor เป็นพูลเธรดภายในดังนั้นจึงสามารถรองรับการดำเนินการพร้อมกันของงานหลายงาน
3.2. แทนที่ตัวจับเวลาด้วย ScheduleDexecutorService
1) แก้ปัญหาหนึ่ง:
ชั้นเรียนสาธารณะ scheduleDexecutortest {ส่วนตัว ScheduleDexecutorService SchedueXec; การเริ่มต้นที่ยาวนานของสาธารณะ ScheduleDexecutortest () {this.schedueXec = executors.newscheduledThreadPool (2); this.start = System.currentTimeMillis (); } โมฆะสาธารณะ timerone () {schedueXec.schedule (ใหม่ runnable () {public void run () {system.out.println ("timerone, เวลา:" + (system.currenttimeMillis (เริ่มต้น)); ลอง {thread.sleep (4000); }, 1,000, timeunit.milliseconds); } โมฆะสาธารณะ timertwo () {scheduleexec.schedule (ใหม่ runnable () {public void run () {system.out.println ("timertwo, เวลา:" + (system.currentTimeMillis () - เริ่มต้น); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {scheduleDexecutortest test = ใหม่ scheduleDexecutortest (); test.timerone (); test.timertwo (); - ผลการทำงาน:
Timerone เวลา: 1003
Timertwo เวลา: 2005
2) แก้ปัญหาสอง
ชั้นเรียนสาธารณะ scheduleDexecutortest {ส่วนตัว ScheduleDexecutorService SchedueXec; การเริ่มต้นที่ยาวนานของสาธารณะ ScheduleDexecutortest () {this.schedueXec = executors.newscheduledThreadPool (2); this.start = System.currentTimeMillis (); } โมฆะสาธารณะ timerone () {schedueXec.schedule (ใหม่ runnable () {public void run () {โยน runtimeException ใหม่ ();}}, 1,000, timeUnit.milliseconds); } โมฆะสาธารณะ timertwo () {scheduleexec.scheduleatfixedrate (ใหม่ runnable () {public void run () {system.out.println ("Timertwo เรียก ... ..... ");}}, 2000,500, TimeUnit.milliseconds); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {scheduleDexecutortest test = ใหม่ scheduleDexecutortest (); test.timerone (); test.timertwo (); - ผลการทำงาน:
Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียก ... Timertwo เรียกใช้ ... Timertwo เรียกใช้ ... Timertwo เรียก ... Timertwo เรียก ... Timertwo
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ นี่คือทั้งหมดที่เกี่ยวกับงานกำหนดเวลา Java ฉันหวังว่ามันจะเป็นประโยชน์กับการเรียนรู้ของคุณ