WatchDog

Thallius

Top Contributor
Hi,

ich habe ein Spring backend, welches eine (oder sogar mehrere, je nachdem wie viele Frontends das anwerfen) sehr lange Berechnung durchführt. Deshalb started dieses einfach einen Thread und das Frontend kann über einen weiteren Request den aktuellen Status abfragen.
Nun kommt es, wie leider bei fast jeder Software, manchmal dazu, dass dieser Thread abstürzt.
Ich möchte das nun mitbekommen um dem Frontend bei seinem Status-Request mitzuteilen das wohl was schief gelaufen ist.

Gibt es eine einfach Möglichkeit einen WatchDog für einen Java Thread aufzusetzen der mir mitteilt wenn der Thread sich mit einer Exception verabschiedet hat?

Gruß

Claus
 

httpdigest

Top Contributor
Aus welchem Grund verabschiedet sich der Thread denn? Wenn es einfach eine "normale" Exception ist, würde ich in der Thread.run() Methode bzw. im Callback einfach einen try{}catch{Exception} wrappen. Damit behandelst du ja den Fall, wenn der Thread abnormal durch eine Exception beendet wird, indem du die Exception fängst und entsprechend Dinge tust, um sie zu behandeln.
 

Thallius

Top Contributor
Aus welchem Grund verabschiedet sich der Thread denn? Wenn es einfach eine "normale" Exception ist, würde ich in der Thread.run() Methode bzw. im Callback einfach einen try{}catch{Exception} wrappen. Damit behandelst du ja den Fall, wenn der Thread abnormal durch eine Exception beendet wird, indem du die Exception fängst und entsprechend Dinge tust, um sie zu behandeln.

Was ist für Dich eine "Normale" Exception. Mit geht es natürlich um die, die man normalerweise nicht selber behandelt wie Null-Pointer, oder ArrayOutOfBounds. Die würde ich in Deinem Fall ja dann auch nicht nach oben durchgereicht bekommen oder?
 

httpdigest

Top Contributor
Eine normale Exception ist für mich alles, was unter java.lang.Exception ist. Also z.B. nicht java.lang.Error wie etwa java.lang.OutOfMemoryError. Das beinhaltet also auch alle unchecked Exceptions wie java.lang.NullPointerException und dergleichen.
Was du aber abfangen möchtest, hängt dann ja davon ab, welchen Fehlerfall du hast und welche du behandeln möchtest. java.lang.OutOfMemoryError sollten man z.B. nicht fangen/behandeln, weil das die gesamte JVM und nicht nur den aktuellen Thread beeinflusst.
 

White_Fox

Top Contributor
Gibt es eine einfach Möglichkeit einen WatchDog für einen Java Thread aufzusetzen der mir mitteilt wenn der Thread sich mit einer Exception verabschiedet hat?
So etwas kannst du dir leicht selber bauen: Nimm z.B. die StopWatch aus Apache Commons lang und mache so etwas:

Java:
public class WatchDog extends Thread{
    private StopWatch stopwatch;
    private int maxMillis;
   
    public WatchDog(int maxMillis){
        this.maxMillis = maxMillis;
    }
   
    @Override
    run(){
        stopwatch = new StopWatch();
        stopwatch.start();
       
        while(true){
            if(stopwatch.getTime(MILLIS) > maxMillis){
                //Schlage Alarm
            }
            Thread.sleet(maxMillis/2);
        }
    }
   
    publi void resetWatchDog(){
        stopwatch.reset();
    }
}

So oder so ähnlich...den Watchdogzugriff mußt du natürlich noch synchronisieren. Wenn dein Thread in einer Endlosschleife festhängt ist es natürlich schlecht, wenn dieser in der Schleife ständig zurückgesetzt wird. ;)
 
Zuletzt bearbeitet:

Thallius

Top Contributor
Eine normale Exception ist für mich alles, was unter java.lang.Exception ist. Also z.B. nicht java.lang.Error wie etwa java.lang.OutOfMemoryError. Das beinhaltet also auch alle unchecked Exceptions wie java.lang.NullPointerException und dergleichen.
Was du aber abfangen möchtest, hängt dann ja davon ab, welchen Fehlerfall du hast und welche du behandeln möchtest. java.lang.OutOfMemoryError sollten man z.B. nicht fangen/behandeln, weil das die gesamte JVM und nicht nur den aktuellen Thread beeinflusst.

Dann muss ich aber doch bis in die kleinste Untermethode rein einen Throws Exception hinzufügen. Das ist doch nicht wirklich guter Stil oder?
 

httpdigest

Top Contributor
Dann muss ich aber doch bis in die kleinste Untermethode rein einen Throws Exception hinzufügen.
Nein, musst du nicht. Einfach eine einzige try-catch-Klammer ganz außen:
Java:
public void starteThread() {
  new Thread(() -> {
    try {
      wasDuVorherImThreadGemachtHast();
    } catch (Exception e) {
      // Hier die neue Fehlerbehandlung. Logging, an Client senden, was auch immer.
    }
  }).start();
}
 

Thallius

Top Contributor
Nein, musst du nicht. Einfach eine einzige try-catch-Klammer ganz außen:
Java:
public void starteThread() {
  new Thread(() -> {
    try {
      wasDuVorherImThreadGemachtHast();
    } catch (Exception e) {
      // Hier die neue Fehlerbehandlung. Logging, an Client senden, was auch immer.
    }
  }).start();
}

Das habe ich probiert. Habe einfach in irgendeiner Methode einen IndexOutOfBound erzeugt. Der kommt nicht beim try/catch um den thread.start an
 

httpdigest

Top Contributor
Naja, um den Aufruf von Thread.start() ist auch ziemlich sinnlos. Da wird keine Exception gefangen werden können, weil ja eben ein neuer Thread gestartet wird und die start() Methode von java.lang.Thread selbst nicht throwt... Du musst das genauso machen wie ich es gezeigt habe. Nämlich innerhalb des Threads, also als äußerste Aktion innerhalb des Thread Runnables.
Also nicht so:
Java:
try {
  new Thread(...).start();
} catch (Exception e) {
  // <- hier wird nichts gefangen, weil Thread.start() selber die Exception ja nicht wirft, sondern der Thread Runnable.
}
sondern so wie ich es im Beispiel gezeigt hatte.
 

Thallius

Top Contributor
Dann habe ich die Exceptoin aber ja nur innerhalb des Threads und der weiß ja nix vom seinem Erzeuger. Der Erzeuger muss ja wissen das der Thread gecrashed ist
 

White_Fox

Top Contributor
Observer?

Java:
public class ThalliusConfusingThread extends Thread{
    private Threadobserver threadobserver;
    
    public ThalliusConfusingThread(Threadobserver threadobserver){
        this.threadobserver = threadobserver;
    }
    
    @Override
    public void run(){
        try{
            //...what ever you want
        }
        catch(Exception e){
            threadobserver.reportProblem();
        }
    }
}
 

httpdigest

Top Contributor
Aaaalso... du hast ja dann zwei unterschiedliche Threads. Den "Erzeuger", wie du ihn nennst, und den eigentlichen asynchron dazu laufenden, erzeugten Thread. Hier greifen dann also wieder alle Primitive, wie du zwei Threads miteinander synchronisieren kannst.
1. Soll der Erzeuger-Thread "warten", bis der erzeugte Thread normal oder abnormal (durch eine Exception) beendet wurde?
2. Was soll der Erzeuger-Thread tun, wenn der erzeugte Thread normal oder abnormal beendet wurde?
3. Willst du die Information, dass der erzeugte Thread normal oder abnormal beendet wurde, per Synchronisation an den Erzeuger-Thread kommunizieren oder per Shared Memory?
4. Soll das, was getan werden soll, wenn der erzeugte Thread sich normal oder abnormal beendet, im Kontext des sich beendeten Threads ausgeführt werden, oder im Kontext des Erzeuger-Threads (Beispiel von @White_Fox mit Observer)

Skizziere doch einfach mal, was du überhaupt machen möchtest?

EDIT: Also, wenn man sich deinen initialen Post nochmal durchliest, dann schreibst du ja, dass das Frontend per Polling den aktuellen Status der Bearbeitung abfragen kann. Das heißt, dass aktuell der Hintergrund-Thread per Shared Memory (oder Datenbank) sowieso schon irgendwie die Information bereitgestellt hat, wie weit seine Berechnung schon ist, damit der Request-Handler-Thread für den Frontend-Request diesen Status auslesen kann. Dasselbe musst du doch jetzt einfach nur im Fall tun, wenn die Hintergrundberechnung eben abgebrochen wird. Dann scheibst du halt als Status "abgebrochen" dort hin, wo du aktuell sowieso den Status hingeschrieben hättest. Irgendeine Art von Thread-übergreifender Kommunikation muss doch sowieso aktuell auch schon passieren, damit das Frontend den Status der Berechnung erfragen kann. Ich verstehe nicht, wieso du hierfür ein separates "Watchdog"-Konzept haben möchtest. Lass das doch den Thread tun, der sowieso die Berechnung durchführt.
 
Zuletzt bearbeitet:

Thallius

Top Contributor
Ok,

ich versuche es mal so einfach wie möglich darzustellen:

Ich habe ein Spring Boot Framework. Dort habe ich zwei Entry-Points

Code:
@PostMapping(path="/plan") // Map ONLY POST Requests
public @ResponseBody ResponseData startPlanning (@RequestBody PlanningParameter planningParameter)
@GetMapping(path="/status") // Map ONLY GET Requests
public @ResponseBody ResponseData getPlanningStatus (@RequestParam int instanceNumber

Der Client ruft nun zuerst das startPlanning auf. Das erzeugt eine neue Klasse Planner() und ruft dort das startPlan() auf, welches einfach nur einen Thread startet und dann zurück kommt. Die Instanz dieser Klasse wird dann in eine globale Liste gehängt und ist durch eine instanceNumber eindeutig definiert. Dieser Planner erhält in den PlanningParametern die Info für welches Land er die Planung machen soll. Es können nun mehrere Länder gleichzeitig eine Planung starten, es darf aber keine zweite Planung für ein Land gestartet werden. Deshalb prüft startPlanning halt ob in der Liste bereits eine Planung für das Land exisitert und gibt einen "Planning already running" error zurück falls das so ist.

Der Client welcher die Planung gestartet hat bekommt nun die instanceNumber zurück und kann sich per polling oder was auch immer mit getPlanningStatus(instanceNumber) darüber informieren wie weit denn der Planner so ist. Dafür ist der die Planner Klasse ein Listener von dem Thread und bekommt von diesem ständig ein update was dieser gerade macht. Das speichert der Planner und gibt es bei Anfrage an den Client zurück.

Wenn nun der Thread abstürzt, dann habe ich ein Problem. Denn die Planner Klasse hängt immer noch in meiner globalen Liste und von daher ist es nicht möglich eine neue Plannung zu starten, da startPlanning() ja meint es läuft noch eine. Die Planner Klasse muss also wissen wenn der Thread abgestürzt ist und entsprechend muss ich das in getPlanningStatus() abfragen können um gegebenenfalls den Planner aus der Liste zu werfen.
 

httpdigest

Top Contributor
Wenn nun der Thread abstürzt, dann habe ich ein Problem. Denn die Planner Klasse hängt immer noch in meiner globalen Liste und von daher ist es nicht möglich eine neue Plannung zu starten, da startPlanning() ja meint es läuft noch eine. Die Planner Klasse muss also wissen wenn der Thread abgestürzt ist und entsprechend muss ich das in getPlanningStatus() abfragen können um gegebenenfalls den Planner aus der Liste zu werfen.
Naja, wie entfernst du denn aktuell schon den Planner, wenn der Task erfolgreich durchgelaufen ist? Lass doch den Thread seinen Planner selber aus der Liste entfernen. Z.B. mit einem finally {} innerhalb des Planner-Threads oder eben per catch().
Java:
new Thread(() -> {
  try {
    machWasImmerDuImThreadBerechnenWillst();
    setzeStatusDesPlanners(Status.ERFOLGREICH);
  } catch (Exception e) {
    setzeStatusDesPlanners(Status.FEHLERHAFT);
  } finally {
    entfernePlannerAusListe();
  }
}).start();
 

mrBrown

Super-Moderator
Mitarbeiter
Du arbeitest doch auch sicher nicht mit einem einfachen Thread#start sondern irgendwas drauf aufsetzendes, Spring bietet ja genug?

Im Einfachsten Fall mit @Async kannst du ein CompletableFuture zurückbekommen und bekommst darüber die geworfene Exception.
 

Thallius

Top Contributor
Wie kommunizierst du denn dann aktuell, wenn/dass der Thread fertig ist, wenn er denn erfolgreich war und was genau tust du dann, um den Planner aus der Liste zu entfernen?

Ok, ich habe es jetzt soweit. Gibt nur noch eine Sache die ich nicht verstehe. Wenn ich im catch block ein e.getMessage() mache steht dort dann nur "5". Gibt es da keine richtige Message bei IndexOutOfBounds Exceptions?
 

httpdigest

Top Contributor
Ok, ich habe es jetzt soweit. Gibt nur noch eine Sache die ich nicht verstehe. Wenn ich im catch block ein e.getMessage() mache steht dort dann nur "5". Gibt es da keine richtige Message bei IndexOutOfBounds Exceptions?
Meine Vermutung ist, dass du dir selbst eine IndexOutOfBounds Exception baust mit "5" als Message und die dann schmeisst. Wieso testen Leute ihre Vermutung denn nicht einfach mal, statt in einem Forum eine Frage dazu zu schreiben?!

Schnell geschriebener Test:
Java:
public class ExceptionsTest {
    public static void main(String[] args) {
        try {
            schmeisseManuelleException();
        } catch (Exception e) {
            System.out.println(e.getMessage()); // <- 5
        }
        try {
            nutzeEingebauteMethodeDieExceptionSchmeisst();
        } catch (Exception e) {
            System.out.println(e.getMessage()); // <- Index 4 out of bounds for length 0
        }
    }
    private static void schmeisseManuelleException() {
        throw new IndexOutOfBoundsException("5");
    }
    private static void nutzeEingebauteMethodeDieExceptionSchmeisst() {
        new ArrayList<>().get(4);
    }
}
 

Thallius

Top Contributor
Danke das du mich für so dumm hälst. Meine Exception erzeugt ich richtig:

int[] t = new int[2];
int b = t[5];

Hier bekomme ich halt nur die 5 und keinen weiteren Text dazu. Macht nicht wirklich Sinn
 

httpdigest

Top Contributor
Danke das du mich für so dumm hälst.
Ich halte dich nicht für dumm, sondern die meisten Leute nur für faul. :)
Und Leuten muss man immer alles aus der Nase ziehen. Z.B. hast du erst jetzt den Code, der die Exception tatsächlich produziert, genannt. Das hättest du schon gleich bei deiner Frage, warum nur "5" als Message herauskommt, tun können. Dann müssen sich andere Leute nicht wundern, woher das denn nun kommen mag.

EDIT: Allerdings kommt bei deinem Array-Beispiel bei mir auch eine vernünftige Exception-Nachricht "Index 5 out of bounds for length 2". Das ist also offensichtlich nicht der Code, der die Exception schmeisst (was du in einem einfachen Test auch hättest prüfen können).
 

Thallius

Top Contributor
Und schon die zweite haltlose Unterstellung. Natuerlich habe ich einen Breakpoint an der Stelle und ja die wirft die Exception aber mir wirds jetzt hier echt zu blöd.

Bin raus
 

httpdigest

Top Contributor
:) Nicht böse gemeint. Sorry!
Anscheinend wirft JDK8 hier nur die "5" als Exception-Nachricht.
Ich habe es ursprünglich mit einem JDK11 ausprobiert. Da kommt eine bessere Nachricht.
Also nochmal sorry, wenn ich dich verägert hatte.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
serjoscha WatchDog Thread und Listener Allgemeine Java-Themen 10

Ähnliche Java Themen

Neue Themen


Oben