Behandlung von InterruptedException in Java

317

Was ist der Unterschied zwischen den folgenden Arten der Handhabung InterruptedException? Was ist der beste Weg, um es zu tun?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

ODER

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

EDIT: Ich möchte auch wissen, in welchen Szenarien diese beiden verwendet werden.

devnull
quelle

Antworten:

435

Was ist der Unterschied zwischen den folgenden Methoden zur Behandlung von InterruptedException? Was ist der beste Weg, um es zu tun?

Sie sind wahrscheinlich gekommen, um diese Frage zu stellen, weil Sie eine Methode aufgerufen haben, die wirft InterruptedException.

Zunächst sollten Sie sehen, throws InterruptedExceptionwas es ist: Ein Teil der Methodensignatur und ein mögliches Ergebnis des Aufrufs der von Ihnen aufgerufenen Methode. Beginnen Sie also damit, die Tatsache zu berücksichtigen, dass an InterruptedExceptionein perfekt gültiges Ergebnis des Methodenaufrufs ist.

Was sollte Ihre Methode tun , wenn die von Ihnen aufgerufene Methode eine solche Ausnahme auslöst? Sie können die Antwort herausfinden, indem Sie über Folgendes nachdenken:

Ist es für die Methode, die Sie implementieren , sinnvoll , eine zu werfen InterruptedException? Anders ausgedrückt ist einInterruptedException vernünftiges Ergebnis beim Aufrufen Ihrer Methode?

  • Wenn ja , dann throws InterruptedExceptionsollte ein Teil von sein Ihrer Methodensignatur sein, und Sie sollten die Ausnahme verbreiten lassen (dh sie überhaupt nicht abfangen).

    Beispiel : Ihre Methode wartet auf einen Wert aus dem Netzwerk, um die Berechnung abzuschließen und ein Ergebnis zurückzugeben. Wenn der blockierende Netzwerkaufruf eine InterruptedExceptionauslöst, kann Ihre Methode die Berechnung nicht auf normale Weise beenden. Sie lassen das InterruptedExceptionverbreiten.

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
  • Wenn nein , sollten Sie Ihre Methode nicht mit deklarieren throws InterruptedExceptionund die Ausnahme abfangen (müssen!). In dieser Situation sind nun zwei Dinge zu beachten:

    1. Jemand hat Ihren Thread unterbrochen. Dieser Jemand ist wahrscheinlich bestrebt, den Vorgang abzubrechen, das Programm ordnungsgemäß zu beenden oder was auch immer. Sie sollten höflich zu dieser Person sein und ohne weiteres von Ihrer Methode zurückkehren.

    2. Auch wenn Ihre Methode im Falle einer InterruptedExceptionUnterbrechung des Threads einen vernünftigen Rückgabewert erzielen kann, kann dies dennoch von Bedeutung sein. Insbesondere der Code, der Ihre Methode aufruft, kann daran interessiert sein, ob während der Ausführung Ihrer Methode eine Unterbrechung aufgetreten ist. Sie sollten daher die Tatsache protokollieren, dass eine Unterbrechung stattgefunden hat, indem Sie das unterbrochene Flag setzen:Thread.currentThread().interrupt()

    Beispiel : Der Benutzer hat aufgefordert, eine Summe von zwei Werten zu drucken. Das Drucken von " Failed to compute sum" ist akzeptabel, wenn die Summe nicht berechnet werden kann (und viel besser, als das Programm aufgrund eines Stapels mit einem Stack-Trace abstürzen zu lassen InterruptedException). Mit anderen Worten, es ist nicht sinnvoll, diese Methode mit zu deklarieren throws InterruptedException.

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }

Inzwischen sollte klar sein, dass throw new RuntimeException(e)es eine schlechte Idee ist , nur etwas zu tun . Es ist nicht sehr höflich zum Anrufer. Sie könnten eine neue Laufzeitausnahme erfinden, aber die Grundursache (jemand möchte, dass der Thread die Ausführung stoppt) geht möglicherweise verloren.

Andere Beispiele:

ImplementierungRunnable : Wie Sie vielleicht festgestellt haben, Runnable.runerlaubt die Signatur von kein erneutes Werfen InterruptedExceptions. Nun, Sie haben sich bei der Implementierung angemeldet Runnable, was bedeutet, dass Sie sich für die Bearbeitung angemeldet haben InterruptedExceptions. Wählen Sie entweder eine andere Schnittstelle aus, z. B. Callableoder folgen Sie dem zweiten Ansatz oben.

 

AnrufenThread.sleep : Sie versuchen, eine Datei zu lesen, und die Spezifikation besagt, dass Sie es 10 Mal mit 1 Sekunde dazwischen versuchen sollten. Du rufst an Thread.sleep(1000). Sie müssen sich also darum kümmern InterruptedException. Für ein Verfahren, wie tryToReadFilees Sinn macht , um zu sagen : „Wenn ich unterbrochen werde ich nicht meine Aktion zu versuchen , die Datei ausführen kann lesen“ . Mit anderen Worten, es ist absolut sinnvoll, die Methode zu werfen InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

Dieser Beitrag wurde bereits als ein Artikel neu geschrieben hier .

aioobe
quelle
Was ist das Problem mit der zweiten Methode?
Blitzkriegz
4
Der Artikel besagt, dass Sie anrufen sollten interrupt(), um den unterbrochenen Status beizubehalten. Was ist der Grund dafür, dies nicht auf einem zu tun Thread.sleep()?
Oksayt
7
Ich bin nicht einverstanden, dass in dem Fall, in dem Ihr Thread keine Interrupts unterstützt, empfangene Interrupts auf einen unerwarteten Zustand hinweisen (z. B. einen Programmierfehler, schlechte API-Verwendung), und das Aussteigen mit einer RuntimeException eine angemessene Antwort ist (Fail-Fast) ). Sofern dies nicht als Teil des allgemeinen Vertrags eines Threads betrachtet wird, müssen Sie den Betrieb fortsetzen, selbst wenn Sie keine Interrupts unterstützen, wenn Sie diese erhalten.
Amoe
12
Das Aufrufen von Methoden, die InterruptedExceptions auslösen und sagen, dass Sie "keine Interrupts unterstützen", scheint eine schlampige Programmierung und ein Fehler zu sein, der darauf wartet, passiert zu werden. Anders ausgedrückt; Wenn Sie eine Methode aufrufen, die zum Auslösen von InterruptedException deklariert ist, sollte dies keine unerwartete Bedingung sein, wenn diese Methode tatsächlich eine solche Ausnahme auslöst.
Aioobe
1
@MartiNito, nein, das Aufrufen Thread.currentThread.interrupt()eines InterruptedExceptioncatch-Blocks setzt nur das Interrupt-Flag. Es wird nicht dazu führen, dass ein anderer InterruptedExceptionin einen äußeren InterruptedExceptionFangblock geworfen / gefangen wird .
Aioobe
97

Zufällig habe ich heute Morgen auf meinem Weg zur Arbeit in Java Concurrency In Practice von Brian Goetz gerade darüber gelesen . Grundsätzlich sagt er, Sie sollten eines von drei Dingen tun

  1. Propagieren Sie dasInterruptedException - Deklarieren Sie Ihre Methode, um das Häkchen zu werfen, InterruptedExceptiondamit Ihr Anrufer damit umgehen muss.

  2. Interrupt wiederherstellen - Manchmal kann man nicht werfen InterruptedException. In diesen Fällen sollten Sie InterruptedExceptionden Interrupt-Status abfangen und wiederherstellen, indem Sie die interrupt()Methode auf dem aufrufen, currentThreaddamit der Code weiter oben im Aufrufstapel erkennt, dass ein Interrupt ausgegeben wurde, und schnell von der Methode zurückkehren. Hinweis: Dies gilt nur, wenn Ihre Methode über die Semantik "Try" oder "Best Effort" verfügt, dh nichts Kritisches passiert, wenn die Methode ihr Ziel nicht erreicht. Zum Beispiel log()oder sendMetric()kann eine solche Methode sein oder boolean tryTransferMoney(), aber nicht void transferMoney(). Sehen Sie hier für weitere Details.

  3. Ignorieren Sie die Unterbrechung innerhalb der Methode, stellen Sie jedoch den Status beim Beenden wieder her - z Uninterruptibles. B. über Guavas . UninterruptiblesÜbernehmen Sie den Boilerplate-Code wie im Beispiel für nicht stornierbare Aufgaben in JCIP § 7.1.3.
mR_fr0g
quelle
10
"Manchmal kann man keine InterruptedException auslösen " - ich würde sagen, manchmal ist es nicht angemessen, dass die Methode InterruptedExceptions propagiert. Ihre Formulierung scheint darauf hinzudeuten, dass Sie die InterruptedException erneut auslösen sollten, wann immer Sie können .
Aioobe
1
Und wenn Sie bekommt Kapitel 7 zu lesen, werden Sie sehen , dass es einige Feinheiten dazu, wie die in Listing 7.7, wo eine unkündbare Aufgabe nicht den Interrupt nicht wiederherstellen sofort , sondern erst , nachdem es fertig ist. Auch Goetz hat viele Co-Autoren zu diesem Buch ...
Fizz
1
Ich mag Ihre Antwort, weil sie prägnant ist, aber ich glaube, es sollte auch heißen, im zweiten Fall sanft und schnell von Ihrer Funktion zurückzukehren. Stellen Sie sich eine lange (oder unendliche) Schleife mit einem Thread.sleep () in der Mitte vor. Der Fang der InterruptedException sollte Thread.currentThread (). Interrupt () aufrufen und aus der Schleife ausbrechen.
Yann Vo
@YannVo Ich habe die Antwort bearbeitet, um Ihren Vorschlag anzuwenden.
Leventov
18

Was versuchst du zu machen?

Das InterruptedExceptionwird ausgelöst, wenn ein Thread wartet oder schläft und ein anderer Thread ihn mit der interruptMethode in der Klasse unterbricht Thread. Wenn Sie diese Ausnahme abfangen, bedeutet dies, dass der Thread unterbrochen wurde. Normalerweise macht es keinen Sinn anzurufenThread.currentThread().interrupt(); erneut , es sei denn, Sie möchten den "unterbrochenen" Status des Threads von einer anderen Stelle aus überprüfen.

In Bezug auf Ihre andere Möglichkeit, ein zu werfen RuntimeException, scheint es nicht sehr klug zu sein (wer wird das fangen? Wie wird es gehandhabt?), Aber es ist schwierig, ohne zusätzliche Informationen mehr zu erzählen.

Grodriguez
quelle
14
Das Aufrufen Thread.currentThread().interrupt()setzt das unterbrochene Flag (erneut), was in der Tat nützlich ist, wenn sichergestellt werden soll, dass der Interrupt auf einer höheren Ebene bemerkt und verarbeitet wird.
Péter Török
1
@ Péter: Ich habe gerade die Antwort aktualisiert, um dies zu erwähnen. Vielen Dank.
Grodriguez
12

Für mich ist das Wichtigste daran: Eine InterruptedException ist nichts, was schief geht, es ist der Thread, der das tut, was Sie ihm gesagt haben. Daher macht es keinen Sinn, es erneut in eine RuntimeException zu werfen.

In vielen Fällen ist es sinnvoll, eine in eine RuntimeException eingeschlossene Ausnahme erneut auszulösen, wenn Sie sagen, ich weiß nicht, was hier schief gelaufen ist, und ich kann nichts tun, um das Problem zu beheben. Ich möchte nur, dass es aus dem aktuellen Verarbeitungsablauf herauskommt und klicken Sie auf den anwendungsweiten Ausnahmebehandler, den ich habe, damit er protokolliert werden kann. Dies ist bei einer InterruptedException nicht der Fall. Es handelt sich lediglich um den Thread, der auf den Aufruf von Interrupt () reagiert. Er löst die InterruptedException aus, um die Verarbeitung des Threads rechtzeitig abzubrechen.

Verbreiten Sie also die InterruptedException oder essen Sie sie intelligent (dh an einem Ort, an dem sie das erreicht hat, was sie tun sollte) und setzen Sie das Interrupt-Flag zurück. Beachten Sie, dass das Interrupt-Flag gelöscht wird, wenn die InterruptedException ausgelöst wird. Die Entwickler der Jdk-Bibliothek gehen davon aus, dass das Abfangen der Ausnahme der Behandlung entspricht. Daher wird das Flag standardmäßig gelöscht.

Der erste Weg ist also definitiv besser. Das zweite Beispiel in der Frage ist nur dann nützlich, wenn Sie nicht erwarten, dass der Thread tatsächlich unterbrochen wird, und wenn Sie ihn unterbrechen, ist dies ein Fehler.

Hier ist eine Antwort, die ich geschrieben habe und die beschreibt, wie Interrupts funktionieren, anhand eines Beispiels . Sie können im Beispielcode sehen, wo die InterruptedException verwendet wird, um aus einer while-Schleife in der Run-Methode von Runnable auszusteigen.

Nathan Hughes
quelle
10

Die richtige Standardauswahl ist das Hinzufügen von InterruptedException zu Ihrer Wurfliste. Ein Interrupt zeigt an, dass ein anderer Thread wünscht, dass Ihr Thread endet. Der Grund für diese Anfrage wird nicht offensichtlich und ist vollständig kontextbezogen. Wenn Sie also keine zusätzlichen Kenntnisse haben, sollten Sie davon ausgehen, dass es sich nur um ein freundliches Herunterfahren handelt, und alles, was dieses Herunterfahren vermeidet, ist eine nicht freundliche Antwort.

Java wird InterruptedException's nicht zufällig auslösen. Alle Ratschläge wirken sich nicht auf Ihre Anwendung aus, aber ich bin auf einen Fall gestoßen, in dem Entwickler, die der "Swallow" -Strategie folgen, sehr unpraktisch wurden. Ein Team hatte eine große Anzahl von Tests entwickelt und Thread.Sleep viel verwendet. Jetzt haben wir begonnen, die Tests auf unserem CI-Server auszuführen, und manchmal steckten aufgrund von Fehlern im Code permanente Wartezeiten fest. Um die Situation zu verschlimmern, wurde beim Versuch, den CI-Job abzubrechen, nie geschlossen, da der Thread.Interrupt, der den Test abbrechen sollte, den Job nicht abbrach. Wir mussten uns in die Box einloggen und die Prozesse manuell beenden.

Kurz gesagt, wenn Sie einfach die InterruptedException auslösen, stimmen Sie mit der Standardabsicht überein, dass Ihr Thread enden soll. Wenn Sie InterruptedException nicht zu Ihrer Wurfliste hinzufügen können, würde ich sie in eine RuntimeException einschließen.

Es gibt ein sehr rationales Argument dafür, dass InterruptedException selbst eine RuntimeException sein sollte, da dies eine bessere "Standard" -Behandlung fördern würde. Es ist keine RuntimeException, nur weil die Designer an einer kategorischen Regel festhielten, dass eine RuntimeException einen Fehler in Ihrem Code darstellen sollte. Da eine InterruptedException nicht direkt aus einem Fehler in Ihrem Code entsteht, ist dies nicht der Fall. Die Realität ist jedoch, dass häufig eine InterruptedException auftritt, weil ein Fehler in Ihrem Code vorliegt (z. B. Endlosschleife, Deadlock), und der Interrupt eine Methode eines anderen Threads zur Behandlung dieses Fehlers ist.

Wenn Sie wissen, dass eine rationale Bereinigung durchgeführt werden muss, dann tun Sie es. Wenn Sie eine tiefere Ursache für den Interrupt kennen, können Sie eine umfassendere Behandlung übernehmen.

Zusammenfassend sollte Ihre Auswahl für die Handhabung dieser Liste folgen:

  1. Standardmäßig zu Würfen hinzufügen.
  2. Wenn Sie keine Würfe hinzufügen dürfen, werfen Sie RuntimeException (e). (Beste Auswahl mehrerer schlechter Optionen)
  3. Nur wenn Sie eine explizite Ursache für den Interrupt kennen, gehen Sie wie gewünscht vor. Wenn Ihre Behandlung lokal für Ihre Methode ist, setzen Sie das Zurücksetzen unterbrochen durch einen Aufruf von Thread.currentThread (). Interrupt ().
Nedruod
quelle
3

Ich wollte nur eine letzte Option zu dem hinzufügen, was die meisten Leute und Artikel erwähnen. Wie mR_fr0g angegeben hat, ist es wichtig, den Interrupt korrekt zu behandeln, entweder durch:

  • Weitergabe der InterruptException

  • Stellen Sie den Interrupt-Status des Threads wieder her

Oder zusätzlich:

  • Benutzerdefinierte Behandlung von Interrupt

Es ist nichts Falsches daran, den Interrupt abhängig von Ihren Umständen auf benutzerdefinierte Weise zu behandeln. Da ein Interrupt eine Anforderung zur Beendigung ist, im Gegensatz zu einem erzwungenen Befehl, ist es durchaus gültig, zusätzliche Arbeiten abzuschließen, damit die Anwendung die Anforderung ordnungsgemäß verarbeiten kann. Wenn ein Thread beispielsweise in den Ruhezustand wechselt und auf E / A oder eine Hardwareantwort wartet, wenn er den Interrupt empfängt, ist es vollkommen gültig, alle Verbindungen ordnungsgemäß zu schließen, bevor der Thread beendet wird.

Ich empfehle dringend, das Thema zu verstehen, aber dieser Artikel ist eine gute Informationsquelle: http://www.ibm.com/developerworks/java/library/j-jtp05236/

Das es
quelle
0

Ich würde in einigen Fällen sagen, dass es in Ordnung ist, nichts zu tun. Wahrscheinlich nicht etwas, das Sie standardmäßig tun sollten, aber falls es keine Möglichkeit für den Interrupt geben sollte, bin ich mir nicht sicher, was ich sonst tun soll (wahrscheinlich Protokollierungsfehler, aber das hat keinen Einfluss auf den Programmfluss).

Ein Fall wäre, wenn Sie eine Task-Blockierwarteschlange haben. Falls Sie einen Daemon-Thread haben, der diese Aufgaben erledigt, und Sie den Thread nicht selbst unterbrechen (meines Wissens unterbricht der JVM die Daemon-Threads beim Herunterfahren des JVM nicht), sehe ich keine Möglichkeit für den Interrupt, und daher könnte es sein einfach ignoriert. (Ich weiß, dass ein Daemon-Thread jederzeit vom JVM beendet werden kann und daher in einigen Fällen ungeeignet ist).

BEARBEITEN: Ein anderer Fall könnten geschützte Blöcke sein, zumindest basierend auf dem Tutorial von Oracle unter: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

Bjarne Boström
quelle
Das ist ein schlechter Rat. Ich habe extreme Probleme, an eine Aufgabe zu denken, die so wichtig ist, dass Interrupts ignoriert werden. Oracle war wahrscheinlich nur faul und wollte dem Aufruf von Thread.sleep () keine (mehr) Unordnung hinzufügen. Wenn Sie nicht wissen, warum Ihr Thread unterbrochen wird, sollten Sie aufhören, was Sie tun (entweder zurückkehren oder eine Ausnahme erneut auslösen, damit Ihr Thread so schnell wie möglich stirbt).
Ajax
Deshalb habe ich manchmal gesagt. Auch weil es noch nicht vorgeschlagen wurde und es meiner Meinung nach in einigen Fällen vollkommen in Ordnung ist . Ich denke, es hängt davon ab, welche Art von Software Sie erstellen, aber wenn Sie einen 24/7-Worker-Thread haben, der niemals aufhören sollte (abgesehen vom Herunterfahren der JVM), würde ich Interrupts, z. B. das Entfernen eines Elements aus einer BlockingQueue, als Fehler behandeln (dh log) da "es sollte nicht passieren" und versuchen Sie es erneut. Natürlich hätte ich separate Flags, um die Programmbeendigung zu überprüfen. In einigen meiner Programme sollte das Herunterfahren von JVM im normalen Betrieb nicht passieren.
Bjarne Boström
Oder anders ausgedrückt: Meiner Meinung nach ist es besser, zusätzliche Fehlerprotokollanweisungen (und z. B. das Senden von E-Mails in Fehlerprotokollen) zu riskieren, als eine Anwendung zu haben, die aufgrund einer Interruptexzeption rund um die Uhr ausgeführt werden sollte Ich sehe keine Möglichkeit, unter normalen Bedingungen zu geschehen.
Bjarne Boström
Hm, nun, Ihre Anwendungsfälle mögen unterschiedlich sein, aber in einer idealen Welt beenden Sie nicht nur, weil Sie unterbrochen wurden. Sie nehmen die Arbeit nicht mehr aus der Warteschlange und lassen DIESEN Thread sterben. Wenn der Thread-Scheduler ebenfalls unterbrochen und zum Herunterfahren aufgefordert wird, wird die JVM beendet und das Spiel beendet. Insbesondere wenn Sie kill -9 senden oder auf andere Weise versuchen, Ihren Mailserver oder was auch immer herunterzufahren, werden Sie ignoriert, bis Sie das Beenden erzwingen, wodurch verhindert wird, dass andere Teile Ihres Codes ordnungsgemäß heruntergefahren werden. Force Kill beim Schreiben auf Festplatte oder Datenbank, und ich bin sicher, dass wunderbare Dinge passieren werden.
Ajax