Ich versuche jeden Tag um 5 Uhr morgens eine bestimmte Aufgabe auszuführen. Also habe ich mich dafür entschieden, ScheduledExecutorService
aber bisher habe ich Beispiele gesehen, die zeigen, wie man alle paar Minuten eine Aufgabe ausführt.
Und ich kann kein Beispiel finden, das zeigt, wie man eine Aufgabe jeden Tag zu einer bestimmten Zeit (5 Uhr morgens) ausführt und auch die Sommerzeit berücksichtigt -
Unten ist mein Code, der alle 15 Minuten ausgeführt wird -
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();
}
}
Gibt es eine Möglichkeit, eine Aufgabe so zu planen, dass sie jeden Tag um 5 Uhr morgens ausgeführt wird, wobei auch ScheduledExecutorService
die Sommerzeit berücksichtigt wird?
Und TimerTask
ist auch besser dafür oder ScheduledExecutorService
?
Antworten:
Wie bei der aktuellen Java SE 8-Version mit der hervorragenden Datums- und Uhrzeit-API kann
java.time
diese Art der Berechnung einfacher durchgeführt werden, anstattjava.util.Calendar
und zu verwendenjava.util.Date
.Als Beispiel für die Planung einer Aufgabe mit Ihrem Anwendungsfall:
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);
Das
initalDelay
wird berechnet, um den Scheduler zu bitten, die Ausführung in zu verzögernTimeUnit.SECONDS
. Zeitdifferenzprobleme mit Millisekunden und darunter scheinen für diesen Anwendungsfall vernachlässigbar zu sein. Aber man kann immer noch Gebrauch machenduration.toMillis()
undTimeUnit.MILLISECONDS
die Planungs computaions in Millisekunden für die Handhabung.NEIN:
ScheduledExecutorService
scheinbar besser alsTimerTask
. StackOverflow hat bereits eine Antwort für Sie .Von @PaddyD,
Da es stimmt und @PaddyD ihm bereits eine Problemumgehung gegeben hat (+1 für ihn), biete ich ein Arbeitsbeispiel mit der Java8-Datums- / Uhrzeit-API mit
ScheduledExecutorService
. Die Verwendung eines Daemon-Threads ist gefährlichclass 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); } } }
Hinweis:
MyTask
ist eine Schnittstelle mit Funktionexecute
.ScheduledExecutorService
immerawaitTermination
nach dem Aufrufen verwendenshutdown
: Es besteht immer die Wahrscheinlichkeit, dass Ihre Aufgabe stecken bleibt / blockiert und der Benutzer für immer warten würde.Das vorherige Beispiel, das ich mit Calender gegeben habe, war nur eine Idee, die ich erwähnt habe. Ich habe genaue Zeitberechnungen und Probleme mit der Sommerzeit vermieden. Die Lösung wurde gemäß der Beschwerde von @PaddyD aktualisiert
quelle
intDelayInHour
ich meine Aufgabe um 5 Uhr morgens ausführen kann?scheduleAtFixedRate
wird es nicht schneiden, es sei denn, Sie sind das ganze Jahr über mit der gleichen UTC-Zeit zufrieden.In 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);
quelle
TimeUnit.DAYS.toMinutes(1)
anstelle der "magischen Zahl" 1440Wenn Sie nicht den Luxus haben, Java 8 verwenden zu können, können Sie Folgendes tun:
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(); } }
Es erfordert keine externen Bibliotheken und berücksichtigt die Sommerzeit. Geben Sie einfach die Tageszeit ein, zu der Sie die Aufgabe als
Calendar
Objekt und die Aufgabe als Objekt ausführen möchtenRunnable
. Zum Beispiel: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");
Hinweis: Die Timer-Task wird in einem Daemon-Thread ausgeführt, der für E / A-Vorgänge nicht empfohlen wird. Wenn Sie einen Benutzer-Thread verwenden müssen, müssen Sie eine weitere Methode hinzufügen, mit der der Timer abgebrochen wird.
Wenn Sie a verwenden müssen
ScheduledExecutorService
, ändern Sie einfach diestartTimer
Methode wie folgt:private void startTimer() { Executors.newSingleThreadExecutor().schedule(new Runnable() { Thread.currentThread().setName(runThreadName); dailyTask.run(); startTimer(); }, getNextRunTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); }
Ich bin mir des Verhaltens nicht sicher, aber Sie benötigen möglicherweise eine Stoppmethode, die aufgerufen wird,
shutdownNow
wenn Sie dieScheduledExecutorService
Route entlang gehen . Andernfalls kann Ihre Anwendung hängen bleiben, wenn Sie versuchen, sie zu stoppen.quelle
new Timer(runThreadName, true)
) verwenden.Haben Sie darüber nachgedacht, so etwas wie Quartz Scheduler zu verwenden ? Diese Bibliothek verfügt über einen Mechanismus zum Planen von Aufgaben, die jeden Tag zu einem festgelegten Zeitpunkt ausgeführt werden sollen, wobei ein Cron-ähnlicher Ausdruck verwendet wird (siehe
CronScheduleBuilder
).Einige Beispielcodes (nicht getestet):
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(); } }
Es gibt ein bisschen mehr Arbeit im Voraus, und Sie müssen möglicherweise Ihren Jobausführungscode neu schreiben, aber es sollte Ihnen mehr Kontrolle darüber geben, wie Ihr Job ausgeführt werden soll. Es wäre auch einfacher, den Zeitplan zu ändern, falls dies erforderlich sein sollte.
quelle
Java8:
Meine Upgrage-Version von oben Antwort:
/** * 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"); } } }
quelle
Ich hatte ein ähnliches Problem. Ich musste eine Reihe von Aufgaben planen, die während eines Tages ausgeführt werden sollten
ScheduledExecutorService
. Dies wurde durch eine Aufgabe gelöst, die um 3.30 Uhr begann und alle anderen Aufgaben relativ zu seiner aktuellen Zeit plante . Und sich für den nächsten Tag um 3:30 Uhr neu zu planen.In diesem Szenario ist die Sommerzeit kein Problem mehr.
quelle
Sie können eine einfache Datumsanalyse verwenden. Wenn die Tageszeit vorher liegt, beginnen wir morgen:
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);
quelle
Nur um Victors Antwort zusammenzufassen .
Ich würde empfehlen, eine Prüfung hinzuzufügen, um festzustellen, ob die Variable (in seinem Fall die lange
midnight
) höher als ist1440
. Wenn ja, würde ich das weglassen.plusDays(1)
, sonst wird die Aufgabe erst übermorgen ausgeführt.Ich habe es einfach so gemacht:
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; }
quelle
truncatedTo()
Das folgende Beispiel funktioniert für mich
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); } }
Referenz: https://chynten.wordpress.com/2016/06/03/java-scheduler-to-run-every-day-on-specific-time/
quelle
Sie können unter Klasse verwenden, um Ihre Aufgabe jeden Tag zu einer bestimmten Zeit zu planen
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()); } }
quelle
Date
undTimeUnit
im Jahr 2019Was ist, wenn Ihr Server um 4:59 Uhr ausfällt und um 5:01 Uhr zurückkommt? Ich denke, es wird nur den Lauf überspringen. Ich würde einen dauerhaften Planer wie Quartz empfehlen, der seine Zeitplandaten irgendwo speichert. Dann wird es sehen, dass dieser Lauf noch nicht durchgeführt wurde und wird es um 5:01 Uhr tun.
quelle