ScheduleAtFixedRate เทียบกับ scheduleWithFixedDelay


118

อะไรคือความแตกต่างที่สำคัญระหว่างscheduleAtFixedRateและscheduleWithFixedDelayวิธีการของScheduledExecutorService ?

scheduler.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println("scheduleAtFixedRate:    " + new Date());
    }
}, 1, 3L , SECONDS);

scheduler.scheduleWithFixedDelay(new Runnable() {
    @Override
    public void run() {
        System.out.println("scheduleWithFixedDelay: " + new Date());
    }
}, 1, 3L , SECONDS);

พวกเขาพิมพ์ในเวลาเดียวกันอย่างแน่นอนดูเหมือนว่าจะถูกดำเนินการในช่วงเวลาเดียวกัน

คำตอบ:


207

ลองเพิ่มThread.sleep(1000);การโทรภายในของคุณrun()วิธีการ ... โดยทั่วไปมันเป็นความแตกต่างระหว่างการตั้งเวลาบางสิ่งบางอย่างขึ้นอยู่กับเมื่อการดำเนินการก่อนหน้านี้จะสิ้นสุดลงและเมื่อมัน (เหตุผล) เริ่มต้น

ตัวอย่างเช่นสมมติว่าฉันตั้งเวลาให้นาฬิกาปลุกดังขึ้นโดยใช้อัตราคงที่ชั่วโมงละครั้งและทุกครั้งที่ดังขึ้นฉันจะดื่มกาแฟหนึ่งแก้วซึ่งใช้เวลา 10 นาที สมมติว่าเริ่มตอนเที่ยงคืนฉันมี:

00:00: Start making coffee
00:10: Finish making coffee
01:00: Start making coffee
01:10: Finish making coffee
02:00: Start making coffee
02:10: Finish making coffee

หากฉันกำหนดเวลาด้วยความล่าช้าคงที่หนึ่งชั่วโมงฉันจะมี:

00:00: Start making coffee
00:10: Finish making coffee
01:10: Start making coffee
01:20: Finish making coffee
02:20: Start making coffee
02:30: Finish making coffee

สิ่งที่คุณต้องการขึ้นอยู่กับงานของคุณ


18
จะเกิดอะไรขึ้นในสถานการณ์ fixedRate หากใช้เวลามากกว่าหนึ่งชั่วโมงในการชงกาแฟ
Brett VanderVeen

5
@BrettVanderVeen: ฉันเชื่อว่ามันขึ้นอยู่กับตัวดำเนินการที่มีปัญหา มันจะถูกกำหนดเวลา - แต่การดำเนินการนั้นขึ้นอยู่กับว่าเธรดพร้อมใช้งานสำหรับตัวดำเนินการนั้นหรือไม่ ฉันขอแนะนำให้คุณทดลองเพื่อดูว่ามันทำงานอย่างไรในสถานการณ์ต่างๆ
Jon Skeet

8
@BrettVanderVeen จากเอกสาร "หากการดำเนินการใด ๆ ของงานนี้ใช้เวลานานกว่าระยะเวลาการดำเนินการในภายหลังอาจเริ่มล่าช้า แต่จะไม่ดำเนินการพร้อมกัน" กล่าวอีกนัยหนึ่งการใช้งานที่สอดคล้องกันจะไม่อนุญาตให้ดำเนินการต่อไปจนกว่าการดำเนินการก่อนหน้าจะเสร็จสิ้น
M. Justin

คุณสามารถระบุรหัสการทำงานสำหรับผลลัพธ์ที่แสดง (กาแฟ) สำหรับมือใหม่อย่างฉันได้หรือไม่?
MuneshSingh

@MuneshSingh: ไม่ใช่ในคำถามนี้ซึ่งขอให้อธิบายว่าความแตกต่างระหว่างการตั้งเวลาในอัตราคงที่กับการตั้งเวลาที่ความล่าช้าคงที่คืออะไร คุณจะไม่ใช้สิ่งนี้ด้วยตัวเอง - คุณจะใช้ตัวดำเนินการในตัว
Jon Skeet

57

แสดงภาพอนุกรมเวลาของscheduleAtFixedRateวิธีการเรียกใช้ การประหารชีวิตครั้งต่อไปจะเริ่มขึ้นทันทีหากการดำเนินการครั้งสุดท้ายใช้เวลานานกว่าช่วงเวลา มิฉะนั้นจะเริ่มหลังจากช่วงเวลาหนึ่ง

อนุกรมเวลาของวิธีการเรียกใช้ ScheduleAtFixedRate

อนุกรมเวลาของscheduleWithFixedDelayวิธีการเรียกใช้ การดำเนินการถัดไปจะเริ่มต้นหลังจากเวลาล่าช้าระหว่างการยุติการดำเนินการหนึ่งและการเริ่มดำเนินการถัดไปโดยไม่คำนึงถึงเวลาดำเนินการ

อนุกรมเวลาของวิธีการเรียกใช้ ScheduleWithFixedDelay

หวังว่าจะช่วยคุณได้


ฉันไม่เข้าใจคำว่า "พิเศษ" ที่กล่าวถึงในแผนภาพอนุกรมเวลา ScheduleAtFixedRate
MuneshSingh

1
@MuneshSingh หมายถึงการแสดงให้เห็นว่าเวลาในการดำเนินการของงานนานกว่าที่กำหนดไว้ดังนั้นจึงต้องใช้เวลา "พิเศษ" และการดำเนินการต่อไปจะเริ่มทันที
Viorel

@Viorel ขอบคุณสำหรับการชี้แจง หมายความว่า "ช่วงเวลา" ไม่ใช่การหน่วงเวลาที่แน่นอนระหว่างการดำเนินการสองครั้งติดต่อกัน
MuneshSingh

1
@MuneshSingh ช่วงเวลาได้รับการแก้ไข แต่จะไม่หยุดงานปัจจุบันเมื่อผ่านไปแล้วจะไม่มีความล่าช้าระหว่างการวิ่งนี้และครั้งต่อไป หากคุณต้องการสร้าง "การหมดเวลา" คุณอาจต้องการคงอนาคตไว้และยกเลิกในตัวดำเนินการอื่น ในคำง่ายๆก็กล่าวเริ่มดำเนินการครั้งแรกและครั้งหนึ่งต่อไปโดยเร็วที่สุดเท่าที่เป็นไปได้หลังจากที่ "ระยะเวลา" เวลาผ่านไป
Viorel

4

scheduleAtFixedRate()วิธีการสร้างงานใหม่และส่งไปยังผู้ปฏิบัติการระยะเวลาที่ทุกคนโดยไม่คำนึงถึงหรือไม่ว่างานที่แล้วเสร็จ

บนมืออื่น ๆ ที่scheduleWithFixedDelay()วิธีการสร้างงานใหม่หลังจากที่งานก่อนหน้านี้ได้เสร็จสิ้น


คุณเขียนสองครั้งscheduleAtFixedRate:)
Vlad

3

ถ้าคุณอ่าน Java Doc มันจะชัดเจนขึ้น

ScheduledFuture ScheduleAtFixedRate (คำสั่ง Runnable, long initialDelay, long period, TimeUnit unit) สร้างและเรียกใช้การดำเนินการเป็นระยะที่เปิดใช้งานก่อนหลังจากการหน่วงเวลาเริ่มต้นที่กำหนดและตามมาด้วยช่วงเวลาที่กำหนด นั่นคือการดำเนินการจะเริ่มหลังจาก initialDelay แล้ว initialDelay + period จากนั้น initialDelay + 2 * period และอื่น ๆ

ScheduledFuture scheduleWithFixedDelay (คำสั่ง Runnable, long initialDelay, long delay, TimeUnit unit) สร้างและเรียกใช้การดำเนินการเป็นระยะที่เปิดใช้งานก่อนหลังจากการหน่วงเวลาเริ่มต้นที่กำหนดและต่อมาด้วยความล่าช้าที่กำหนดระหว่างการสิ้นสุดของการดำเนินการหนึ่งครั้งและการเริ่มต้นของการดำเนินการถัดไป


1

มีหนึ่งรายการที่จับได้ใน ScheduleAtFixedRate หากเธรดแรกใช้เวลานานเกินไปและไม่สิ้นสุดในระยะเวลาที่กำหนดเธรดคอนคัตทีฟที่สองจะไม่เริ่มทำงานเมื่องานแรกจะเสร็จสิ้นและจะไม่เริ่มต้นทันทีในขณะที่เธรดแรกหมดภารกิจและระยะเวลา gievn ผ่านไปแล้ว JVM จะตัดสินใจว่าจะดำเนินการงานต่อไปเมื่อใด

ฉันคิดว่าจะช่วยคุณในการเลือกวิธีการเนื่องจากสิ่งนี้ฉันมีปัญหาใหญ่


1
อะไร? JVM จะตัดสินใจ? มันควรจะหมายถึงอะไร? เป็นความจริงที่ runnable จะไม่ได้รับการดำเนินการพร้อมกับตัวมันเองตามเอกสาร แต่ถูกกำหนดโดยผู้ดำเนินการซึ่งอาจเป็นแบบกำหนดเองหรือเป็นมาตรฐานScheduledThreadPoolExecutor(และหลังมีพฤติกรรมที่กำหนดไว้อย่างดี)
ธรรมดา

ไม่ฉันพบปัญหาที่คล้ายกันในแอปพลิเคชันของฉันซึ่งฉันให้ช่วงเวลา 15 นาทีและงานแรกไม่เสร็จใน 15 นาทีและใช้เวลา 15.30 วินาทีดังนั้นงานที่สองจึงไม่ได้เริ่มในทันทีมันเริ่มต้นหลังจาก 5 นาทีและหลังจากนั้นไม่นาน 8 นาทีฉันไม่รู้ว่าเราสามารถควบคุมพฤติกรรมนี้ได้หรือไม่เพราะนี่ไม่ใช่พฤติกรรมมาตรฐาน
user1047873

ฟังดูเหมือนการจัดคิวงานตำรา
ธรรมดา

ใช่หมายความว่าเธรดทั้งหมดในตัวดำเนินการของคุณกำลังยุ่งอยู่กับการทำบางสิ่งบางอย่างและงานของคุณจะอยู่ในคิวของสิ่งที่ต้องทำ ( หมายเหตุคุณต้องยืนยันสิ่งนี้โดยดูที่คิวดังกล่าวหรือดูว่าเธรดตัวดำเนินการกำลังทำอะไร) วิธีที่คุณควบคุมสิ่งนี้ขึ้นอยู่กับประเภทของตัวดำเนินการที่คุณมี คุณอาจต้องการสร้างตัวดำเนินการ 1 เธรดแยกต่างหากสำหรับงานนี้เท่านั้นจากนั้นจะไม่รออะไรเลย หรือเพิ่มเธรดให้กับผู้ดำเนินการปัจจุบันของคุณ หรือเปลี่ยนกลยุทธ์
ธรรมดา

0

มาเขียนโปรแกรมง่ายๆ:

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

var time = 0L
var start = System.currentTimeMillis()
val executor = Executors.newScheduledThreadPool(1)
executor.scheduleWithFixedDelay({
    if (time >= 12_000L) {
        executor.shutdown()
    } else {
        Thread.sleep(2000L)
        val now = System.currentTimeMillis()
        time += now - start
        System.out.println("Total $time delay ${now - start}\n")
        start = now
    }
}, 0L, 1000L, TimeUnit.MILLISECONDS)

และดูผลลัพธ์:

| scheduleWithFixedDelay |   scheduleAtFixedRate  |
|:----------------------:|:----------------------:|
| Total 2001 delay 2001  | Total 2003 delay 2003  |
| Total 5002 delay 3001  | Total 4004 delay 2001  |
| Total 8003 delay 3001  | Total 6004 delay 2000  |
| Total 11003 delay 3000 | Total 8004 delay 2000  |
| Total 14003 delay 3000 | Total 10005 delay 2001 |
|          ---           | Total 12005 delay 2000 |

โปรดสังเกตว่าเวลาดำเนินการใหญ่กว่าที่รอ

ScheduleWithFixedDelayช่วยให้ delay
scheduleAtFixedRateลบความล่าช้า


-1
scheduledExecutorService.scheduleAtFixedRate(() -> {
        System.out.println("runnable start"); try { Thread.sleep(5000);  System.out.println("runnable end");} catch
     (InterruptedException e) { // TODO Auto-generated catch block
      e.printStackTrace(); }}, 2, 7, TimeUnit.SECONDS);



     scheduledExecutorService.scheduleWithFixedDelay(() -> {
     System.out.println("runnable start"); try { Thread.sleep(5000); System.out.println("runnable end");} catch
     (InterruptedException e) { // TODO Auto-generated catch block
     e.printStackTrace(); } }, 2, 7, TimeUnit.SECONDS);

เพียงแค่ดำเนินการและคุณจะรู้ถึงความแตกต่าง ขอบคุณ


1
โปรดอธิบายด้วยว่าโค้ดแก้ปัญหาของ OP ได้อย่างไร :)
Yash
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.