จะเรียกใช้งานบางอย่างทุกวันในช่วงเวลาใดเวลาหนึ่งโดยใช้ ScheduledExecutorService ได้อย่างไร?


90

ฉันพยายามทำงานบางอย่างทุกวันตอนตี 5 ตอนเช้า ดังนั้นฉันจึงตัดสินใจใช้ScheduledExecutorServiceสำหรับสิ่งนี้ แต่จนถึงตอนนี้ฉันได้เห็นตัวอย่างที่แสดงวิธีเรียกใช้งานทุกสองสามนาที

และฉันไม่พบตัวอย่างใด ๆ ที่แสดงวิธีการทำงานทุกวันในช่วงเวลาใดเวลาหนึ่ง (ตี 5) ในตอนเช้าและยังพิจารณาถึงความเป็นจริงของเวลาออมแสงด้วย -

ด้านล่างนี้คือรหัสของฉันซึ่งจะทำงานทุก ๆ 15 นาที -

public class ScheduledTaskExample {
    private final ScheduledExecutorService scheduler = Executors
        .newScheduledThreadPool(1);

    public void startScheduleTask() {
    /**
    * not using the taskHandle returned here, but it can be used to cancel
    * the task, or check if it's done (for recurring tasks, that's not
    * going to be very useful)
    */
    final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(
        new Runnable() {
            public void run() {
                try {
                    getDataFromDatabase();
                }catch(Exception ex) {
                    ex.printStackTrace(); //or loggger would be better
                }
            }
        }, 0, 15, TimeUnit.MINUTES);
    }

    private void getDataFromDatabase() {
        System.out.println("getting data...");
    }

    public static void main(String[] args) {
        ScheduledTaskExample ste = new ScheduledTaskExample();
        ste.startScheduleTask();
    }
}

มีวิธีใดบ้างที่ฉันสามารถกำหนดเวลาให้งานทำงานทุกวัน 5.00 น. ในตอนเช้าโดยScheduledExecutorServiceพิจารณาจากความเป็นจริงของเวลาออมแสงด้วย

และยังTimerTaskดีกว่านี้หรือScheduledExecutorService?


ใช้ควอตซ์แทน
มิลลิโมส

คำตอบ:


113

เช่นเดียวกับการเปิดตัว java SE 8 ในปัจจุบันพร้อมด้วย API วันเวลาที่ยอดเยี่ยมพร้อมjava.timeการคำนวณประเภทนี้สามารถทำได้ง่ายขึ้นแทนที่จะใช้ java.util.Calendarและjava.util.Date.

ตอนนี้เป็นตัวอย่างสำหรับการจัดตารางงานด้วยกรณีการใช้งานของคุณ:

ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0);
if(now.compareTo(nextRun) > 0)
    nextRun = nextRun.plusDays(1);

Duration duration = Duration.between(now, nextRun);
long initalDelay = duration.getSeconds();

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);            
scheduler.scheduleAtFixedRate(new MyRunnableTask(),
    initalDelay,
    TimeUnit.DAYS.toSeconds(1),
    TimeUnit.SECONDS);

คำนวณเพื่อขอให้จัดตารางเวลาที่จะชะลอการดำเนินการในinitalDelay TimeUnit.SECONDSปัญหาความแตกต่างของเวลากับหน่วยมิลลิวินาทีและด้านล่างดูเหมือนจะไม่สำคัญสำหรับกรณีการใช้งานนี้ แต่คุณยังสามารถใช้ประโยชน์duration.toMillis()และTimeUnit.MILLISECONDSจัดการการคำนวณการตั้งเวลาได้ในหน่วยมิลลิวินาที

และ TimerTask จะดีกว่าสำหรับสิ่งนี้หรือ ScheduledExecutorService?

NO: ดูเหมือนจะดีกว่าScheduledExecutorService StackOverflow มีคำตอบให้คุณแล้วTimerTask

จาก @PaddyD,

คุณยังคงมีปัญหาอยู่โดยที่คุณต้องรีสตาร์ทปีละสองครั้งหากคุณต้องการให้มันทำงานในเวลาท้องถิ่นที่ถูกต้อง ScheduleAtFixedRate จะไม่ตัดออกเว้นแต่คุณจะพอใจกับเวลา UTC เดียวกันตลอดทั้งปี

ในฐานะที่มันเป็นความจริงและ @PaddyD แล้วได้รับการแก้ปัญหา (+1 กับเขา) ผมกำลังให้เป็นตัวอย่างการทำงานร่วมกับ Java8 ScheduledExecutorServiceวันเวลากับ การใช้ daemon thread เป็นสิ่งที่อันตราย

class MyTaskExecutor
{
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    MyTask myTask;
    volatile boolean isStopIssued;

    public MyTaskExecutor(MyTask myTask$) 
    {
        myTask = myTask$;

    }

    public void startExecutionAt(int targetHour, int targetMin, int targetSec)
    {
        Runnable taskWrapper = new Runnable(){

            @Override
            public void run() 
            {
                myTask.execute();
                startExecutionAt(targetHour, targetMin, targetSec);
            }

        };
        long delay = computeNextDelay(targetHour, targetMin, targetSec);
        executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);
    }

    private long computeNextDelay(int targetHour, int targetMin, int targetSec) 
    {
        LocalDateTime localNow = LocalDateTime.now();
        ZoneId currentZone = ZoneId.systemDefault();
        ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
        ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
        if(zonedNow.compareTo(zonedNextTarget) > 0)
            zonedNextTarget = zonedNextTarget.plusDays(1);

        Duration duration = Duration.between(zonedNow, zonedNextTarget);
        return duration.getSeconds();
    }

    public void stop()
    {
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException ex) {
            Logger.getLogger(MyTaskExecutor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

บันทึก:

  • MyTaskexecuteเป็นอินเตอร์เฟซที่มีฟังก์ชั่น
  • ในขณะที่หยุดScheduledExecutorServiceให้ใช้ทุกครั้งawaitTerminationหลังจากเรียกใช้shutdown: มีความเป็นไปได้ที่งานของคุณจะติดขัด / หยุดชะงักและผู้ใช้จะรอตลอดไป

ตัวอย่างก่อนหน้านี้ที่ฉันให้กับปฏิทินเป็นเพียงความคิดที่ฉันได้กล่าวถึงฉันหลีกเลี่ยงปัญหาการคำนวณเวลาที่แน่นอนและการประหยัดเวลาตามฤดูกาล อัปเดตวิธีแก้ปัญหาตามคำร้องเรียนของ @PaddyD


ขอบคุณสำหรับข้อเสนอแนะโปรดอธิบายรายละเอียดให้ฉันทราบได้ไหมintDelayInHourว่าฉันจะทำงานตอนตี 5 ตอนเช้าได้อย่างไร
AKIWEB

จุดประสงค์ของ aDate คืออะไร?
José Andias

แต่ถ้าคุณเริ่มที่ HH: mm งานจะถูกรันที่ 05: mm ตรงข้ามกับ 5am? นอกจากนี้ยังไม่คำนึงถึงเวลาออมแสงตามที่ OP ร้องขอ ตกลงถ้าคุณเริ่มทันทีหลังจากชั่วโมงหรือถ้าคุณพอใจกับเวลาใด ๆ ระหว่าง 5 ถึง 6 หรือถ้าคุณไม่คิดจะรีสตาร์ทแอปพลิเคชันในตอนกลางคืนปีละสองครั้งหลังจากที่นาฬิกาเปลี่ยนไปฉันคิดว่า ...
PaddyD

2
คุณยังคงมีปัญหาอยู่โดยที่คุณต้องรีสตาร์ทปีละสองครั้งหากต้องการให้ทำงานในเวลาท้องถิ่นที่ถูกต้อง scheduleAtFixedRateจะไม่ตัดมันเว้นแต่คุณจะมีความสุขกับเวลา UTC เดียวกันตลอดทั้งปี
PaddyD

3
เหตุใดตัวอย่างต่อไปนี้ (วินาที) จึงเรียกใช้งาน n ครั้งหรือจนกว่าวินาทีจะผ่านไป รหัสนี้ไม่ควรเรียกใช้งานวันละครั้งหรือไม่?
krizajb

25

ใน Java 8:

scheduler = Executors.newScheduledThreadPool(1);

//Change here for the hour you want ----------------------------------.at()       
Long midnight=LocalDateTime.now().until(LocalDate.now().plusDays(1).atStartOfDay(), ChronoUnit.MINUTES);
scheduler.scheduleAtFixedRate(this, midnight, 1440, TimeUnit.MINUTES);

14
เพื่อความสะดวกในการอ่านฉันขอแนะนำให้TimeUnit.DAYS.toMinutes(1)ใช้แทน "เลขวิเศษ" 1440
ซื่อสัตย์

ขอบคุณวิคเตอร์ ด้วยวิธีนี้ต้องรีสตาร์ทปีละสองครั้งหากต้องการให้ทำงานในเวลาท้องถิ่นที่ถูกต้อง?
invzbl3

อัตราคงที่ไม่ควรเปลี่ยนแปลงเมื่อเวลาท้องถิ่นเปลี่ยนไปหลังจากสร้างแล้วจะกลายเป็นอัตรา
วิกเตอร์

เหตุใดสิ่งนี้จึงทำให้งานของฉันเวลา 23:59:59 น.
krizajb

เพื่อความสะดวกในการอ่านฉันขอแนะนำ 1 แทน 1440 หรือ TimeUnit.DAYS.toMinutes (1) จากนั้นใช้หน่วยเวลา TimeUnit.DAYS ;-)
Kelly Denehy

7

หากคุณไม่มีความสามารถในการใช้ Java 8 อย่างหรูหราสิ่งต่อไปนี้จะทำสิ่งที่คุณต้องการ:

public class DailyRunnerDaemon
{
   private final Runnable dailyTask;
   private final int hour;
   private final int minute;
   private final int second;
   private final String runThreadName;

   public DailyRunnerDaemon(Calendar timeOfDay, Runnable dailyTask, String runThreadName)
   {
      this.dailyTask = dailyTask;
      this.hour = timeOfDay.get(Calendar.HOUR_OF_DAY);
      this.minute = timeOfDay.get(Calendar.MINUTE);
      this.second = timeOfDay.get(Calendar.SECOND);
      this.runThreadName = runThreadName;
   }

   public void start()
   {
      startTimer();
   }

   private void startTimer();
   {
      new Timer(runThreadName, true).schedule(new TimerTask()
      {
         @Override
         public void run()
         {
            dailyTask.run();
            startTimer();
         }
      }, getNextRunTime());
   }


   private Date getNextRunTime()
   {
      Calendar startTime = Calendar.getInstance();
      Calendar now = Calendar.getInstance();
      startTime.set(Calendar.HOUR_OF_DAY, hour);
      startTime.set(Calendar.MINUTE, minute);
      startTime.set(Calendar.SECOND, second);
      startTime.set(Calendar.MILLISECOND, 0);

      if(startTime.before(now) || startTime.equals(now))
      {
         startTime.add(Calendar.DATE, 1);
      }

      return startTime.getTime();
   }
}

ไม่ต้องใช้ libs ภายนอกใด ๆ และจะช่วยประหยัดเวลากลางวัน เพียงแค่ผ่านไปในช่วงเวลาของวันที่คุณต้องการเรียกใช้งานในรูปแบบCalendarวัตถุและงานเป็นไฟล์Runnable. ตัวอย่างเช่น:

Calendar timeOfDay = Calendar.getInstance();
timeOfDay.set(Calendar.HOUR_OF_DAY, 5);
timeOfDay.set(Calendar.MINUTE, 0);
timeOfDay.set(Calendar.SECOND, 0);

new DailyRunnerDaemon(timeOfDay, new Runnable()
{
   @Override
   public void run()
   {
      try
      {
        // call whatever your daily task is here
        doHousekeeping();
      }
      catch(Exception e)
      {
        logger.error("An error occurred performing daily housekeeping", e);
      }
   }
}, "daily-housekeeping");

NB งานจับเวลาทำงานในเธรด Daemon ซึ่งไม่แนะนำให้ทำ IO ใด ๆ หากคุณจำเป็นต้องใช้เธรดผู้ใช้คุณจะต้องเพิ่มวิธีการอื่นซึ่งจะยกเลิกตัวจับเวลา

หากคุณต้องใช้ a ScheduledExecutorServiceเพียงแค่เปลี่ยนstartTimerวิธีการดังต่อไปนี้:

private void startTimer()
{
   Executors.newSingleThreadExecutor().schedule(new Runnable()
   {
      Thread.currentThread().setName(runThreadName);
      dailyTask.run();
      startTimer();
   }, getNextRunTime().getTime() - System.currentTimeMillis(),
   TimeUnit.MILLISECONDS);
}

ฉันไม่แน่ใจในพฤติกรรมนี้ แต่คุณอาจต้องการวิธีหยุดที่โทรหาshutdownNowหากคุณไปตามScheduledExecutorServiceเส้นทางมิฉะนั้นแอปพลิเคชันของคุณอาจค้างเมื่อคุณพยายามหยุด


ฉันมีประเด็นของคุณ +1 และขอขอบคุณ อย่างไรก็ตามจะดีกว่าถ้าเราไม่ใช้ Daemon thread (เช่นnew Timer(runThreadName, true))
Sage

@Sage ไม่ต้องกังวล เธรด daemon นั้นใช้ได้ถ้าคุณไม่ได้ทำ IO ใด ๆ กรณีการใช้งานที่ฉันเขียนไว้นี้เป็นเพียงคลาสการดับไฟและการลืมที่เรียบง่ายสำหรับการเริ่มต้นเธรดบางส่วนเพื่อทำงานทำความสะอาดประจำวัน ฉันคิดว่าถ้าคุณกำลังดำเนินการฐานข้อมูลอ่านในเธรดงานตัวจับเวลาตามที่คำขอของ OP อาจระบุคุณไม่ควรใช้ Daemon และจะต้องมีวิธีการหยุดบางอย่างซึ่งคุณจะต้องเรียกใช้เพื่อให้แอปพลิเคชันของคุณยุติ stackoverflow.com/questions/7067578/…
PaddyD

@PaddyD เป็นส่วนสุดท้ายคือส่วนที่ใช้ ScheduledExecutorSerive ถูกต้อง ??? วิธีการสร้างคลาสแบบไม่ระบุตัวตนดูไม่ถูกต้องตามหลักไวยากรณ์ นอกจากนี้ newSingleThreadExecutor () จะไม่มีวิธีกำหนดการใช่หรือไม่?
FReeze FRancis

6

คุณคิดจะใช้อะไรอย่างQuartz Schedulerหรือไม่? ไลบรารีนี้มีกลไกในการจัดกำหนดการงานให้ทำงานในช่วงเวลาที่กำหนดทุกวันโดยใช้นิพจน์ cron like (ดูที่CronScheduleBuilder)

โค้ดตัวอย่างบางส่วน (ไม่ได้ทดสอบ):

public class GetDatabaseJob implements InterruptableJob
{
    public void execute(JobExecutionContext arg0) throws JobExecutionException
    {
        getFromDatabase();
    }
}

public class Example
{
    public static void main(String[] args)
    {
        JobDetails job = JobBuilder.newJob(GetDatabaseJob.class);

        // Schedule to run at 5 AM every day
        ScheduleBuilder scheduleBuilder = 
                CronScheduleBuilder.cronSchedule("0 0 5 * * ?");
        Trigger trigger = TriggerBuilder.newTrigger().
                withSchedule(scheduleBuilder).build();

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.scheduleJob(job, trigger);

        scheduler.start();
    }
}

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


5

Java8:
เวอร์ชันอัปเกรดของฉันจากคำตอบยอดนิยม:

  1. แก้ไขสถานการณ์เมื่อ Web Application Server ไม่ต้องการหยุดทำงานเนื่องจาก threadpool ที่มีเธรดไม่ได้ใช้งาน
  2. โดยไม่ต้องเรียกซ้ำ
  3. เรียกใช้งานด้วยเวลาท้องถิ่นที่คุณกำหนดเองในกรณีของฉันคือเบลารุสมินสค์


/**
 * Execute {@link AppWork} once per day.
 * <p>
 * Created by aalexeenka on 29.12.2016.
 */
public class OncePerDayAppWorkExecutor {

    private static final Logger LOG = AppLoggerFactory.getScheduleLog(OncePerDayAppWorkExecutor.class);

    private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

    private final String name;
    private final AppWork appWork;

    private final int targetHour;
    private final int targetMin;
    private final int targetSec;

    private volatile boolean isBusy = false;
    private volatile ScheduledFuture<?> scheduledTask = null;

    private AtomicInteger completedTasks = new AtomicInteger(0);

    public OncePerDayAppWorkExecutor(
            String name,
            AppWork appWork,
            int targetHour,
            int targetMin,
            int targetSec
    ) {
        this.name = "Executor [" + name + "]";
        this.appWork = appWork;

        this.targetHour = targetHour;
        this.targetMin = targetMin;
        this.targetSec = targetSec;
    }

    public void start() {
        scheduleNextTask(doTaskWork());
    }

    private Runnable doTaskWork() {
        return () -> {
            LOG.info(name + " [" + completedTasks.get() + "] start: " + minskDateTime());
            try {
                isBusy = true;
                appWork.doWork();
                LOG.info(name + " finish work in " + minskDateTime());
            } catch (Exception ex) {
                LOG.error(name + " throw exception in " + minskDateTime(), ex);
            } finally {
                isBusy = false;
            }
            scheduleNextTask(doTaskWork());
            LOG.info(name + " [" + completedTasks.get() + "] finish: " + minskDateTime());
            LOG.info(name + " completed tasks: " + completedTasks.incrementAndGet());
        };
    }

    private void scheduleNextTask(Runnable task) {
        LOG.info(name + " make schedule in " + minskDateTime());
        long delay = computeNextDelay(targetHour, targetMin, targetSec);
        LOG.info(name + " has delay in " + delay);
        scheduledTask = executorService.schedule(task, delay, TimeUnit.SECONDS);
    }

    private static long computeNextDelay(int targetHour, int targetMin, int targetSec) {
        ZonedDateTime zonedNow = minskDateTime();
        ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec).withNano(0);

        if (zonedNow.compareTo(zonedNextTarget) > 0) {
            zonedNextTarget = zonedNextTarget.plusDays(1);
        }

        Duration duration = Duration.between(zonedNow, zonedNextTarget);
        return duration.getSeconds();
    }

    public static ZonedDateTime minskDateTime() {
        return ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
    }

    public void stop() {
        LOG.info(name + " is stopping.");
        if (scheduledTask != null) {
            scheduledTask.cancel(false);
        }
        executorService.shutdown();
        LOG.info(name + " stopped.");
        try {
            LOG.info(name + " awaitTermination, start: isBusy [ " + isBusy + "]");
            // wait one minute to termination if busy
            if (isBusy) {
                executorService.awaitTermination(1, TimeUnit.MINUTES);
            }
        } catch (InterruptedException ex) {
            LOG.error(name + " awaitTermination exception", ex);
        } finally {
            LOG.info(name + " awaitTermination, finish");
        }
    }

}

1
ฉันมีข้อกำหนดที่คล้ายกัน ฉันต้องจัดตารางงานเป็นเวลาหนึ่งวันในเวลาที่กำหนด เมื่องานตามกำหนดการเสร็จสิ้นให้กำหนดเวลางานสำหรับวันถัดไปตามเวลาที่กำหนด สิ่งนี้ควรดำเนินต่อไปคำถามของฉันคือจะทราบได้อย่างไรว่างานตามกำหนดเวลาเสร็จสมบูรณ์หรือไม่ เมื่อฉันรู้ว่างานตามกำหนดเวลาเสร็จสิ้นแล้วฉันเท่านั้นที่สามารถกำหนดเวลาสำหรับวันถัดไปได้
amitwdh

2

ฉันมีปัญหาที่คล้ายกัน ฉันต้องจัดตารางงานจำนวนมากที่ควรดำเนินการในระหว่างวันโดยใช้ScheduledExecutorServiceไฟล์. นี้ได้รับการแก้ไขโดยหนึ่งงานเริ่มต้นที่ 03:30 การจัดตารางงานอื่น ๆ ค่อนข้างเขาเวลาปัจจุบัน และจัดตารางตัวเองใหม่ในวันรุ่งขึ้นเวลา 03.30 น.

ด้วยการประหยัดเวลาตามสถานการณ์นี้ไม่ใช่ปัญหาอีกต่อไป


2

คุณสามารถใช้การแยกวิเคราะห์วันที่แบบง่ายๆได้หากเวลาของวันก่อนหน้านี้ให้เริ่มในวันพรุ่งนี้:

  String timeToStart = "12:17:30";
  SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss");
  SimpleDateFormat formatOnlyDay = new SimpleDateFormat("yyyy-MM-dd");
  Date now = new Date();
  Date dateToStart = format.parse(formatOnlyDay.format(now) + " at " + timeToStart);
  long diff = dateToStart.getTime() - now.getTime();
  if (diff < 0) {
    // tomorrow
    Date tomorrow = new Date();
    Calendar c = Calendar.getInstance();
    c.setTime(tomorrow);
    c.add(Calendar.DATE, 1);
    tomorrow = c.getTime();
    dateToStart = format.parse(formatOnlyDay.format(tomorrow) + " at " + timeToStart);
    diff = dateToStart.getTime() - now.getTime();
  }

  ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);            
  scheduler.scheduleAtFixedRate(new MyRunnableTask(), TimeUnit.MILLISECONDS.toSeconds(diff) ,
                                  24*60*60, TimeUnit.SECONDS);

1

เพียงเพื่อเพิ่มขึ้นในการตอบของวิกเตอร์

ฉันอยากจะแนะนำให้เพิ่มการตรวจสอบเพื่อดูว่าตัวแปร (ในกรณีของเขายาวmidnight) สูงกว่า1440หรือไม่ ถ้าเป็นเช่นนั้นฉันจะละเว้น.plusDays(1)มิฉะนั้นงานจะทำงานในวันมะรืนนี้เท่านั้น

ฉันทำแบบนี้:

Long time;

final Long tempTime = LocalDateTime.now().until(LocalDate.now().plusDays(1).atTime(7, 0), ChronoUnit.MINUTES);
if (tempTime > 1440) {
    time = LocalDateTime.now().until(LocalDate.now().atTime(7, 0), ChronoUnit.MINUTES);
} else {
    time = tempTime;
}

มันง่ายขึ้นถ้าคุณใช้truncatedTo()
Mark Jeronimus

1

ตัวอย่างต่อไปนี้ใช้ได้ผลสำหรับฉัน

public class DemoScheduler {

    public static void main(String[] args) {

        // Create a calendar instance
        Calendar calendar = Calendar.getInstance();

        // Set time of execution. Here, we have to run every day 4:20 PM; so,
        // setting all parameters.
        calendar.set(Calendar.HOUR, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.AM_PM, Calendar.AM);

        Long currentTime = new Date().getTime();

        // Check if current time is greater than our calendar's time. If So,
        // then change date to one day plus. As the time already pass for
        // execution.
        if (calendar.getTime().getTime() < currentTime) {
            calendar.add(Calendar.DATE, 1);
        }

        // Calendar is scheduled for future; so, it's time is higher than
        // current time.
        long startScheduler = calendar.getTime().getTime() - currentTime;

        // Setting stop scheduler at 4:21 PM. Over here, we are using current
        // calendar's object; so, date and AM_PM is not needed to set
        calendar.set(Calendar.HOUR, 5);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.AM_PM, Calendar.PM);

        // Calculation stop scheduler
        long stopScheduler = calendar.getTime().getTime() - currentTime;

        // Executor is Runnable. The code which you want to run periodically.
        Runnable task = new Runnable() {

            @Override
            public void run() {

                System.out.println("test");
            }
        };

        // Get an instance of scheduler
        final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        // execute scheduler at fixed time.
        scheduler.scheduleAtFixedRate(task, startScheduler, stopScheduler, MILLISECONDS);
    }
}

อ้างอิง: https://chynten.wordpress.com/2016/06/03/java-scheduler-to-run-every-day-on-specific-time/


1

คุณสามารถใช้ชั้นเรียนด้านล่างเพื่อกำหนดเวลางานของคุณทุกวันในช่วงเวลาใดเวลาหนึ่ง

package interfaces;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class CronDemo implements Runnable{

    public static void main(String[] args) {

        Long delayTime;

        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        final Long initialDelay = LocalDateTime.now().until(LocalDate.now().plusDays(1).atTime(12, 30), ChronoUnit.MINUTES);

        if (initialDelay > TimeUnit.DAYS.toMinutes(1)) {
            delayTime = LocalDateTime.now().until(LocalDate.now().atTime(12, 30), ChronoUnit.MINUTES);
        } else {
            delayTime = initialDelay;
        }

        scheduler.scheduleAtFixedRate(new CronDemo(), delayTime, TimeUnit.DAYS.toMinutes(1), TimeUnit.MINUTES);

    }

    @Override
    public void run() {
        System.out.println("I am your job executin at:" + new Date());
    }
}

1
โปรดอย่าใช้สิ่งที่ล้าสมัยDateและTimeUnitในปี 2019
Mark Jeronimus

0

จะเกิดอะไรขึ้นถ้าเซิร์ฟเวอร์ของคุณหยุดทำงานในเวลา 04:59 น. และกลับมาเวลา 05:01 น. ฉันคิดว่ามันจะข้ามการวิ่ง ฉันอยากจะแนะนำตัวกำหนดตารางเวลาแบบถาวรเช่น Quartz ซึ่งจะเก็บข้อมูลกำหนดการไว้ที่ใดที่หนึ่ง จากนั้นจะเห็นว่าการวิ่งนี้ยังไม่ได้ดำเนินการและจะดำเนินการในเวลา 05:01 น.


0

ทำไมต้องทำให้สถานการณ์ซับซ้อนถ้าคุณสามารถเขียนแบบนั้นได้? (ใช่ -> การทำงานร่วมกันต่ำฮาร์ดโค้ด -> แต่เป็นตัวอย่างและน่าเสียดายที่มีวิธีการที่จำเป็น) สำหรับข้อมูลเพิ่มเติมอ่านตัวอย่างโค้ดด้านล่าง;))

package timer.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.*;

public class TestKitTimerWithExecuterService {

    private static final Logger log = LoggerFactory.getLogger(TestKitTimerWithExecuterService.class);

    private static final ScheduledExecutorService executorService 
= Executors.newSingleThreadScheduledExecutor();// equal to => newScheduledThreadPool(1)/ Executor service with one Thread

    private static ScheduledFuture<?> future; // why? because scheduleAtFixedRate will return you it and you can act how you like ;)



    public static void main(String args[]){

        log.info("main thread start");

        Runnable task = () -> log.info("******** Task running ********");

        LocalDateTime now = LocalDateTime.now();

        LocalDateTime whenToStart = LocalDate.now().atTime(20, 11); // hour, minute

        Duration duration = Duration.between(now, whenToStart);

        log.info("WhenToStart : {}, Now : {}, Duration/difference in second : {}",whenToStart, now, duration.getSeconds());

        future = executorService.scheduleAtFixedRate(task
                , duration.getSeconds()    //  difference in second - when to start a job
                ,2                         // period
                , TimeUnit.SECONDS);

        try {
            TimeUnit.MINUTES.sleep(2);  // DanDig imitation of reality
            cancelExecutor();    // after canceling Executor it will never run your job again
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("main thread end");
    }




    public static void cancelExecutor(){

        future.cancel(true);
        executorService.shutdown();

        log.info("Executor service goes to shut down");
    }

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