Java beendet den Executor-Service, sobald eine seiner zugewiesenen Aufgaben aus irgendeinem Grund fehlschlägt


12

Ich benötige einen Dienst, der einige Aufgaben gleichzeitig und im Abstand von 1 Sekunde für 1 Minute ausführt.

Wenn eine der Aufgaben fehlschlägt, möchte ich den Dienst und jede damit ausgeführte Aufgabe mit einer Art Indikator beenden, dass etwas schief gelaufen ist. Wenn andernfalls nach einer Minute alles gut gelaufen ist, wird der Dienst mit einer Anzeige beendet, dass alles gut gelaufen ist.

Zum Beispiel habe ich 2 Funktionen:

Runnable task1 = ()->{
      int num = Math.rand(1,100);
      if (num < 5){
          throw new Exception("something went wrong with this task,terminate");
      }
}

Runnable task2 = ()->{
      int num = Math.rand(1,100)
      return num < 50;
}



ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
task1schedule = scheduledExecutorService.scheduleAtFixedRate(task1, 1, 60, TimeUnit.SECONDS);
task2schedule = scheduledExecutorService.scheduleAtFixedRate(task2, 1, 60, TimeUnit.SECONDS);

if (!task1schedule || !task2schedule) scheduledExecutorService.shutdown();

Irgendwelche Ideen, wie ich das angehen und die Dinge so allgemein wie möglich gestalten soll?


1
Abgesehen von der eigentlichen Frage gibt Math.randes nur wenige Dinge, die keine integrierte API sind. Eine Implementierung von Runnablemuss eine void runDefinition haben. Art von task1/2schedulewäre ScheduledFuture<?>in dem bereitgestellten Kontext. Kommen wir zur eigentlichen Frage: Wie geht es darum, sie zu nutzen awaitTermination? Sie könnten das als tun scheduledExecutorService.awaitTermination(1,TimeUnit.MINUTES);. Wie wäre es alternativ mit der Überprüfung, ob eine der Aufgaben vor ihrem normalen Abschluss abgebrochen wurde : if (task1schedule.isCancelled() || task2schedule.isCancelled()) scheduledExecutorService.shutdown();?
Naman

2
Es macht keinen Sinn, Aufgaben so zu planen, dass sie jede Minute wiederholt werden. Sagen Sie dann, Sie möchten die Aufgaben stoppen, "wenn nach einer Minute alles gut gelaufen ist". Da Sie den Executor in beiden Fällen stoppen, ist es trivial, eine Aufgabe zu planen, die den Executor nach einer Minute herunterfährt. Und die Futures zeigen bereits an, ob etwas schief gelaufen ist oder nicht. Sie haben nicht gesagt, welche andere Art von Indikator Sie wollen.
Holger

Antworten:


8

Die Idee ist, dass die Aufgaben auf ein gemeinsames Objekt TaskCompleteEvent verschoben werden. Wenn sie einen Fehler auslösen, wird der Scheduler gestoppt und alle Aufgaben werden gestoppt.

Sie können die Ergebnisse jeder Aufgabeniteration in den Karten "Fehler" und "Erfolg" überprüfen.

public class SchedulerTest {

    @Test
    public void scheduler() throws InterruptedException {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        TaskCompleteEvent taskCompleteEvent = new TaskCompleteEvent(scheduledExecutorService);
        Runnable task1 = () -> {
            int num = new Random().nextInt(100);
            if (num < 5) {
                taskCompleteEvent.message("task1-"+UUID.randomUUID().toString(), "Num "+num+" was obatined. Breaking all the executions.", true);
            }
        };
        Runnable task2 = () -> {
            int num = new Random().nextInt(100);
            taskCompleteEvent.message("task2-"+UUID.randomUUID().toString(), num < 50, false);
        };
        scheduledExecutorService.scheduleAtFixedRate(task1, 0, 1, TimeUnit.SECONDS);
        scheduledExecutorService.scheduleAtFixedRate(task2, 0, 1, TimeUnit.SECONDS);
        scheduledExecutorService.awaitTermination(60, TimeUnit.SECONDS);
        System.out.println("Success: "+taskCompleteEvent.getSuccess());
        System.out.println("Errors: "+taskCompleteEvent.getErrors());
        System.out.println("Went well?: "+taskCompleteEvent.getErrors().isEmpty());
    }

    public static class TaskCompleteEvent {

        private final ScheduledExecutorService scheduledExecutorService;
        private final Map<String, Object> errors = new LinkedHashMap<>();
        private final Map<String, Object> success = new LinkedHashMap<>();

        public TaskCompleteEvent(ScheduledExecutorService scheduledExecutorService) {
            this.scheduledExecutorService = scheduledExecutorService;
        }

        public synchronized void message(String id, Object response, boolean error) {
            if (error) {
                errors.put(id, response);
                scheduledExecutorService.shutdown();
            } else {
                success.put(id, response);
            }
        }

        public synchronized Map<String, Object> getErrors() {
            return errors;
        }

        public synchronized Map<String, Object> getSuccess() {
            return success;
        }

    }

}

2

Sie müssen nur eine zusätzliche Aufgabe hinzufügen, deren Aufgabe es ist, alle anderen laufenden Aufgaben zu überwachen. Wenn eine der überwachten Aufgaben fehlschlägt, müssen sie ein Semaphor (Flag) setzen, das der Attentäter überprüfen kann.

    ScheduledExecutorService executor = (ScheduledExecutorService) Executors.newScheduledThreadPool(2);

    // INSTANTIATE THE REMOTE-FILE-MONITOR:
    RemoteFileMonitor monitor = new RemoteFileMonitor(remotesource, localtarget);

    // THIS TimerTask PERIODICALLY TRIGGERS THE RemoteFileMonitor: 
    TimerTask remote = new TimerTask() {

        // RUN FORREST... RUN !
        public void run() {

            try { 

                kae.trace("TimerTask::run() --> Calling RemoteFileMonitor.check()");
                monitor.check();

            } catch (Exception ex) {

                // NULL TRAP: ALLOWS US TO CONTINUE AND RETRY:

            }

        }

    };

    // THIS TimerTask PERIODICALLY TRIES TO KILL THE REMOTE-FILE-MONITOR:
    TimerTask assassin = new TimerTask() {

        // WHERE DO BAD FOLKS GO WHEN THEY DIE ? 
        private final LocalDateTime death = LocalDateTime.now().plus(ConfigurationOptions.getPollingCycleTime(), ChronoUnit.MINUTES);

        // RUN FORREST... RUN !
        public void run() {

            // IS THERE LIFE AFTER DEATH ???
            if (LocalDateTime.now().isAfter(death)) {

                // THEY GO TO A LAKE OF FIRE AND FRY:
                kae.error(ReturnCode.MONITOR_POLLING_CYCLE_EXCEEDED);                   

            }

        }

    };

    // SCHEDULE THE PERIODIC EXECUTION OF THE RemoteFileMonitor: (remote --> run() monitor --> check())
    executor.scheduleAtFixedRate(remote, delay, interval, TimeUnit.MINUTES);

    // SCHEDULE PERIODIC ASSASSINATION ATTEMPTS AGAINST THE RemoteFileMonitor: (assassin --> run() --> after death --> die())
    executor.scheduleAtFixedRate(assassin, delay, 60L, TimeUnit.SECONDS);

    // LOOP UNTIL THE MONITOR COMPLETES:
    do {

        try {

            // I THINK I NEED A NAP:
            Thread.sleep(interval * 10);                

        } catch (InterruptedException e) {

            // FAIL && THEN cleanexit();
            kae.error(ReturnCode.MONITORING_ERROR, "Monitoring of the XXXXXX-Ingestion site was interrupted");

        }

        // NOTE: THE MONITOR IS SET TO 'FINISHED' WHEN THE DONE-File IS DELIVERED AND RETRIEVED:
    } while (monitor.isNotFinished());

    // SHUTDOWN THE MONITOR TASK:
    executor.shutdown();

2
Die Klasse TimerTaskhat nichts mit zu tun ScheduledExecutorService; es passiert einfach zu implementieren Runnable. Außerdem macht es keinen Sinn, eine periodische Aufgabe zu planen, nur um zu überprüfen, ob eine bestimmte Zeit ( ConfigurationOptions.getPollingCycleTime()) erreicht wurde. Sie haben eine ScheduledExecutorService, damit Sie festlegen können, dass die Aufgabe für die gewünschte Zeit richtig geplant werden soll.
Holger

Die Implementierung in dem von mir verwendeten Beispiel bestand darin, eine ausführende Aufgabe nach einer bestimmten Zeitspanne zu beenden, wenn die Aufgabe nicht abgeschlossen war. Der Anwendungsfall war: Wenn der Remote-Server innerhalb von 2 Stunden keine Datei gelöscht hat, beenden Sie die Aufgabe. Darum hat das OP gebeten.
Greg Patnude

Hast du meinen Kommentar gelesen und verstanden? Es spielt keine Rolle , was der Code tut, ist es eine entmutigte Klasse ohne Grund verwendet, ersetzen Sie einfach TimerTaskmit Runnableund Sie haben das Problem behoben, ohne Änderung , was der Code tut. Verwenden executor.schedule(assassin, ConfigurationOptions.getPollingCycleTime(), ChronoUnit.MINUTES);Sie einfach und es wird einmal zum gewünschten Zeitpunkt ausgeführt, daher ist die if(LocalDateTime.now().isAfter(death))Prüfung veraltet. Auch hier ändert sich nichts an der Funktionsweise des Codes, außerdem ist es wesentlich einfacher und effizienter.
Holger
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.