2009-09-02 9 views
38

Hey, ich schreibe eine Netzwerkanwendung, in der ich Pakete mit einem benutzerdefinierten Binärformat lese. Und ich beginne einen Hintergrund-Thread, um auf eingehende Daten zu warten. Das Problem ist, dass der Compiler mich nicht erlaubt, irgendwelche Code-werfende (kontrollierte) Ausnahmen in run() zu setzen. Es heißt:Wie man eine geprüfte Ausnahme von einem Java-Thread wirft?

run() in (...).Listener cannot implement run() in java.lang.Runnable; overridden method does not throw java.io.IOException

Ich möchte die Ausnahme den Thread zu töten, und lassen Sie es irgendwo im übergeordneten Thread gefangen werden. Ist das möglich zu erreichen oder muss ich jede Ausnahme behandeln innerhalb der Thread?

+1

Werfen Sie einen Blick auf folgende Antwort: [Festlegen einer Ausnahme von einem Thread fangen] [1] [1]: http://stackoverflow.com/questions/ 6546193/how-to-catch-a-exception-from-a-thread –

Antwort

36

Vorbehalt: Dies entspricht möglicherweise nicht Ihren Anforderungen, wenn Sie den Ausnahmemechanismus verwenden müssen.

Wenn ich Sie richtig verstehe, brauchen Sie die Ausnahme nicht wirklich zu überprüfen (Sie haben die Antwort angenommen, die eine ungeprüfte Ausnahme vorschlägt), also wäre ein einfaches Listenermuster passender?

Der Listener könnte im übergeordneten Thread leben, und wenn Sie die geprüfte Ausnahme im untergeordneten Thread abgefangen haben, können Sie den Listener einfach benachrichtigen.

Dies bedeutet, dass Sie eine Möglichkeit haben, zu enthüllen, dass dies geschieht (durch öffentliche Methoden), und mehr Informationen weitergeben können, als eine Ausnahme zulässt. Aber es bedeutet, dass es eine Kopplung (wenn auch eine lose) zwischen dem Eltern- und dem Kind-Thread geben wird. Es würde in Ihrer speziellen Situation davon abhängen, ob dies einen Vorteil gegenüber dem Umhüllen der geprüften Ausnahme mit einem unkontrollierten haben würde.

Hier ist ein einfaches Beispiel (ein Code von einer anderen Antwort geborgt):

public class ThingRunnable implements Runnable { 
    private SomeListenerType listener; 
    // assign listener somewhere 

    public void run() { 
     try { 
      while(iHaveMorePackets()) { 
       doStuffWithPacket(); 
      } 
     } catch(Exception e) { 
      listener.notifyThatDarnedExceptionHappened(...); 
     } 
    } 
} 

Die Kupplung kommt von einem Objekt in dem übergeordneten Thread des Typs sein zu müssen SomeListenerType.

+0

Gute Idee. Und ja, du hast recht, es ist mir egal, ob die Ausnahme aktiviert ist oder nicht, ich wollte nur einen einfachen Weg, um eine Ausnahme herauszubringen. – mik01aj

+0

Wird der Listener-Code nicht immer noch im untergeordneten Thread ausgeführt? Ich verstehe, dass es jetzt zu viel von dem Bereich in dem übergeordneten Thread hat ... aber es wird weiterhin in dem unterordneten Thread ausgeführt. – Jared

+0

Es würde in den Child-Thread laufen, aber es entspricht, was gefragt wurde, denke ich. – Grundlefleck

3

Wenn Sie beim Auslösen der Ausnahme wirklich nichts Nützliches tun können, können Sie die geprüfte Ausnahme in eine RuntimeException umbrechen.

+4

Immer eine Beschreibung hinzufügen, wenn eine Ausnahme ausgelöst wird, auch wenn sie nur umbrochen werden soll. neue RuntimeException werfen ("Wrapping-Ausnahme, damit sie bis zum Catcher in foo.bar.Main()" platzen kann, e); Die um 3 Uhr morgens angerufenen, wenn der Code bricht, werden es schätzen, wenn sie auf den Stacktrace starren. –

1

Wenn der Code Ihres Threads eine RuntimeExpection auslöst, müssen Sie keine run() - Ausnahme hinzufügen.

Aber verwenden Sie diese Lösung nur, wenn angebracht, weil dies eine schlechte pratice sein kann: http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

Alle Runtime oder deaktiviert Exception kann Ihnen helfen. Vielleicht müssen Sie Ihre eigene RuntimeException erstellen

+2

Beachten Sie, dass dieser Ansatz die Ausnahme aus dem übergeordneten Thread ausblenden wird. – erickson

3

Der Thread kann die Ausnahme nicht zu einem anderen Thread (noch zum Hauptthread) werfen. und Sie können die vererbte run() -Methode nicht dazu bringen, alle überprüften Ausnahmen auszugeben, da Sie nur weniger als den vererbten Code werfen können, nicht mehr.

+0

Guter Punkt darüber, dass die Ausnahme nicht direkt in den übergeordneten Thread "geworfen" werden kann. – erickson

0

Unter der Annahme, dass Ihr Code in einer Art Schleife ist, dann würden Sie schreiben:

public class ThingRunnable implements Runnable { 
    public void run() { 
    try { 
     while(iHaveMorePackets()) { 
     doStuffWithPacket() 
     } 
    } catch(Exception e) { 
     System.out.println("Runnable terminating with exception" + e); 
    } 
    } 
} 

Die Ausnahme, die Sie automatisch aus der Schleife brechen, und am Ende des Laufes() -Methode , der Thread wird aufhören.

+0

Kleiner Punkt: Ihr Beispiel hat eine Schnittstelle mit der Implementierung in es, sollte wahrscheinlich "öffentliche Klasse sein ThingRunnable implementiert Runnable". – Grundlefleck

+0

Danke, Grundlefleck, du hast absolut recht :-) Geht zu zeigen, was passiert, wenn Sie Fragen beantworten, bevor Sie genug Kaffee am Tag gehabt haben. Ich habe den Text aktualisiert, um stattdessen "Klasse" zu sagen. – AlBlue

7

Was ich tue, ist die Ausnahme im Thread zu fangen und speichern Sie es als eine Member-Variable der Runnable. Diese Ausnahme wird dann über einen Getter auf dem Runnable verfügbar gemacht. Ich scanne dann alle Threads vom übergeordneten Objekt, um festzustellen, ob Ausnahmen vorhanden sind, und ergreife die entsprechenden Maßnahmen.

+1

Darf ich fragen (falls meine Antwort falsch ist), warum eine Variable als Getter gespeichert wird und nach ihr sucht, anstatt einen Listener-Mechanismus zu verwenden? – Grundlefleck

+0

Gerechte Frage. Jeder Ansatz funktioniert. Ich denke, ich werde deinen Vorschlag beim nächsten Mal ausprobieren und sehen, ob ich das bevorzuge. –

+0

Siehe meinen Kommentar zu Grundleflecks Antwort - ich glaube, dass diese Lösung die Ausnahmebehandlung wieder in den übergeordneten Thread zurückbringt, während die Lösung von Grundlefleck dies nicht tut. (Grundlefleck behebt Probleme mit dem Bereich - aber dieser behebt Probleme, die wirklich mit dem Thread-Kontext zusammenhängen.) – Jared

45

Lage sein, die Ausnahme von dem übergeordneten Thread zu senden, können Sie Ihren Hintergrund-Thread in einem Callable (es erlaubt wirft auch Ausnahmen geprüft) stellen, die Sie dann auf die submit Methode der some Executor passieren. Die Methode submit gibt eine zurück, die Sie verwenden können, um die Ausnahme zu erhalten (ihre get-Methode wird eine ExecutionException werfen, die die ursprüngliche Ausnahme enthält).

+3

Das sieht mir zu kompliziert aus. Aber danke trotzdem :) – mik01aj

31

Diese Antwort basiert auf Esko Luontola, aber es bietet ein funktionierendes Beispiel.

Im Gegensatz zur run() - Methode der Runnable-Schnittstelle erlaubt die call() - Methode von Callable das Auslösen einiger Ausnahmen. Hier ist ein Implementierungsbeispiel:

public class MyTask implements Callable<Integer> { 

    private int numerator; 
    private int denominator; 

    public MyTask(int n, int d) { 
     this.numerator = n; 
     this.denominator = d; 
    } 

    @Override 
    // The call method may throw an exception 
    public Integer call() throws Exception { 
     Thread.sleep(1000); 
     if (denominator == 0) { 
      throw new Exception("cannot devide by zero"); 
     } else { 
      return numerator/denominator; 
     } 
    } 

} 

Executor stellt einen Mechanismus ein Callable in einem Thread ausgeführt und jede Art von Ausnahmen zu behandeln:

public class Main { 

    public static void main(String[] args) { 

     // Build a task and an executor 
     MyTask task = new MyTask(2, 0); 
     ExecutorService threadExecutor = Executors.newSingleThreadExecutor(); 

     try { 
      // Start task on another thread 
      Future<Integer> futureResult = threadExecutor.submit(task); 

      // While task is running you can do asynchronous operations 
      System.out.println("Something that doesn't need the tasks result"); 

      // Now wait until the result is available 
      int result = futureResult.get(); 
      System.out.println("The result is " + result); 
     } catch (ExecutionException e) { 
      // Handle the exception thrown by the child thread 
      if (e.getMessage().contains("cannot devide by zero")) 
       System.out.println("error in child thread caused by zero division"); 
     } catch (InterruptedException e) { 
      // This exception is thrown if the child thread is interrupted. 
      e.printStackTrace(); 
     } 
    } 
} 
+6

Dies löst das Problem der nicht abgefangenen Ausnahmen, könnte aber eigene Probleme verursachen. Der Aufruf von 'get()' wird blockiert, bis die aufrufbare Aufgabe zurückkehrt. Dies bedeutet, dass Sie keine Parallelität mehr aus dem Hintergrundthread erhalten. Schlimmer noch, wenn die Aufgabe kontinuierlich/lang andauernd ist (wie das Warten auf Netzwerkpakete in einer Schleife), kehrt das 'Callable' _never_ zurück und Ihr Haupt-Thread wird permanent blockiert. Ich möchte nicht implizieren, dass es keine Anwendungen gibt, in denen dieses Muster sinnvoll ist (ich bin sicher, es gibt viele), nur dass Sie vorsichtig sein müssen. – theisenp

+0

Guter Punkt. In einem realen Beispiel würden Sie nicht direkt nach dem Senden "holen" aufrufen. –

+0

In einem realen Beispiel könnten Sie N Hintergrundoperationen parallel initiieren und auf die Ergebnisse von M Hintergrundoperationen warten, wobei M <= N ist, um eine Entscheidung zu treffen. In diesem Fall würden Sie sofort nach dem Senden anrufen. – Ameliorator

-1

Wrapping Ihre Ausnahme innerhalb eines RuntimeException den Trick zu tun scheint .

someMethod() throws IOException 
{ 
    try 
    { 
     new Thread(() -> 
     { 
      try 
      { 
       throw new IOException("a checked exception thrown from within a running thread"); 
      } 
      catch(IOException ex) 
      { 
       throw new RuntimeException("a wrapper exception", ex); // wrap the checked exception inside an unchecked exception and throw it 
      } 
     }).start(); 
    } 
    catch(RuntimeException ex) // catch the wrapped exception sent from within the thread 
    { 
     if(ex.getCause() instanceof IOException) 
      throw ex.getCause; // unwrap the checked exception using getCause method and use it however you need 
     else 
      throw ex; 
    } 
} 
+0

Es funktioniert nicht – Richard

Verwandte Themen