2010-03-26 5 views
16

Wenn ein Thread innerhalb von Object.wait() oder Thread.join() unterbrochen wird, wird ein InterruptedException, ausgelöst, der den unterbrochenen Status des Threads zurücksetzt. . I. e, wenn ich eine Schleife wie diese in einem Runnable.run() haben:Warum unterbrechen InterruptedExceptions den unterbrochenen Status eines Threads?

while (!this._workerThread.isInterrupted()) { 
    // do something 
    try { 
     synchronized (this) { 
      this.wait(this._waitPeriod); 
     } 
    } catch (InterruptedException e) { 
     if (!this._isStopping()) { 
      this._handleFault(e); 
     } 
    } 
} 

der Faden wird auch weiterhin nach dem Aufruf von interrupt() laufen. Das bedeutet, dass ich explizit aus der Schleife ausbrechen muss, indem ich in der Schleifenbedingung nach meinem eigenen Stopp-Flag suche, die Ausnahme erneut auftrage oder eine break hinzufüge.

Nun, das ist nicht wirklich ein Problem, da dieses Verhalten gut dokumentiert ist und mich nicht daran hindert, etwas zu tun, was ich will. Das Konzept dahinter scheint mir jedoch nicht nachvollziehbar zu sein: Warum wird ein Thread nach dem Auslösen der Ausnahme nicht mehr als unterbrochen betrachtet? Ein ähnliches Verhalten tritt auch auf, wenn Sie den unterbrochenen Status mit interrupted() statt isInterrupted() bekommen, dann wird der Thread auch nur einmal unterbrochen angezeigt.

Mache ich hier etwas Ungewöhnliches? Zum Beispiel, ist es häufiger die InterruptedException außerhalb der Schleife zu fangen?

(Auch wenn ich nicht gerade ein Anfänger bin, habe ich dieses „Anfänger“ markiert, weil es wie eine sehr grundlegende Frage mir scheint, es zu betrachten.)

+0

Da das, was du fragst, eine Frage der Meinung ist ("warum ist es so?"), Solltest du wahrscheinlich dieses Community-Wiki erstellen. –

+1

@ Jonathan: Es ist nicht wirklich eine Frage der Meinung. Wenn man sich die API-Dokumente anschaut, ist klar, dass dahinter ein Grundprinzip stehen muss, also gibt es eine richtige Antwort auf meine Frage. Der von mir akzeptierte klingt sehr plausibel (thread safe interrupt handling). –

Antwort

7

Die Idee ist, dass ein Interrupt einmal behandelt werden sollte. Wenn ein explizites InterruptedException das Flag "Interrupt" nicht gelöscht hat, müssten die meisten Catcher für InterruptedException dieses Flag explizit löschen. Umgekehrt kann das Flag durch Selbstunterbrechung "unklar" sein (Thread.currentThread().interrupt()). Die Entwickler von Java haben sich für die Semantik entschieden, die Tastenanschläge am häufigsten speichern würde (d. H. Sie möchten öfter die Flagge löschen, als sie beibehalten).

+2

Ich stimme nicht mit der Logik hinter dem, was vor sich geht, aber sie hätten auf jeden Fall die Methode besser benennen sollen. –

0

das, weil ein InterruptedException ist gilt eine abnorme Ereignis, bei dem ein anderer versucht, einen Thread von außerhalb zu stoppen.

Wenn Sie wirklich einen Thread unterbrechen wollen, brechen Sie einfach seine Schleifenbedingung, indem Sie einen booleschen Wert oder etwas Ähnliches setzen. Oder Sie verwenden .wait() und .notify()aus diesem Thread. Aber wenn Sie tun wait() extern:

  • eine Ausnahme ausgelöst wird, um mitzuteilen, dass ein Außengewinde hat versucht, mich zu unterbrechen oder zu mir
  • der Faden wird ihre Arbeit fortsetzen warten, weil es keine Bestellung dauert von ein anderer Thread! Aber die Erhöhung der Ausnahme ermöglicht es Ihnen, spezielle Handhabung hinzufügen und tun, was Sie wollen, auch den Thread effektiv zu stoppen.
+0

Das ist falsch. 'Thread # isInterrupted' soll der Boolean sein, von dem du gesprochen hast, den man zum loopen verwenden könnte. Durch das Unterbrechen eines Threads wird dieses Bit gesetzt, aber das Abfangen von 'InterruptedException' löscht es und darum ging es in dieser Frage. Wenn Sie die volle Kontrolle über den Thread haben, ist das in Ordnung, da Sie entscheiden, was Sie mit Ihrem eigenen Thread tun möchten, aber wenn Sie Bibliotheks- oder Dienstprogrammcode schreiben, der keine Thread-Verwaltung durchführt und nur von anderem Code aufgerufen wird sehr schlechte Manieren, um das unterbrochene Bit, das jemand gerade gesetzt hat, zurückzusetzen. – mpontes

1

Schreiben Sie Ihren Code wie folgt und Sie erhalten eine Fahne nicht brauchen:

try { 
    while (!this._workerThread.isInterrupted()) { 
     // do something 
     synchronized (this) { 
      this.wait(this._waitPeriod); 
     } 
     // do something else 
    } 
} catch (InterruptedException e) { 
    // ignore ... 
} 

Wie @Boyan weist darauf hin, es ist eine schlechte Idee, dass die Interrupt-Ausnahme zu quetschen ... im Allgemeinen. In diesem Fall bestimmt der Kontext, ob Sie ihn quetschen sollen (wie oben), setzen Sie das Interrupt-Flag (erneut) oder lassen Sie die Ausnahme propagieren. Es hängt unter anderem davon ab, was der Interrupt in Ihrer Anwendung bedeutet.

+0

sollten Sie es nicht einfach ignorieren. In den 90% der Fälle werden Sie mit dem Code, den Sie geschrieben haben, vollkommen in Ordnung sein, aber es besteht die Möglichkeit, dass jemand anderes auf diese Unterbrechung wartet und das Löschen der Flagge wird ihre Logik brechen. Um auf der sicheren Seite zu sein, müssen Sie im Exception-Handler 'Thread.currentThread(). Interrupt()' aufrufen. – Boyan

0

Es sollte nicht.Dies ist ein unglücklicher Konstruktionsfehler, der Unterbrechungen zu einem riskanten Geschäft macht, da Bibliothekscode zu oft InterruptedException fängt, ohne das unterbrochene Flag des Threads zurückzusetzen und weiterzumachen. Wenn Sie Ihrem Thread eine Unterbrechung signalisieren, wenn dieser bestimmte Teil des beschädigten Bibliothekscodes ausgeführt wird und der Code die Ausführungskontrolle wiedererlangt, bleibt er ohne Hinweis darauf, dass die Unterbrechung aufgetreten ist.

Dies muss nur einmal an jedem Ort geschehen, den Sie aus Ihrem Code anrufen, so dass Sie in der Lage sind, einen Thread zu unterbrechen und dann das unterbrochene Bit verwenden, um Ihren Fluss aus dem Thread sicher zu steuern 100% ig sicher sein, dass jedes Stück Code, das Sie anrufen, das unterbrochene Bit nicht versehentlich löscht. Dies ist sehr schwierig, wenn Bibliotheken beteiligt sind, aber selbst wenn Sie für jede einzelne Bibliothek, die Sie in Ihrem Code verwenden, Rechenschaft ablegen könnten, die immer noch nicht für buggy JRE code that can make the same mistake verantwortlich ist.

Die Tatsache, dass nur ein Bibliotheksautor (oder JRE! -Autor) sich nicht um Unterbrechungen kümmert oder daran denkt, um die Logik des Codes zu umgehen, der dies erfordert, zeigt, dass dies die falsche Standardaktion ist. Jemand, der sich nicht um das unterbrochene Bit des Threads kümmert, wird sich wahrscheinlich nicht darum kümmern, es nach dem Abfangen zurückzusetzen InterruptedException - vielleicht wissen sie nicht einmal, dass es existiert! Wenn das Abfangen von InterruptedException den unterbrochenen Status des Threads nicht zurückgesetzt hat, dann würde jeder, der nichts über das unterbrochene Bit wusste, automatisch "das Richtige tun" und kein Problem für irgendeinen Anrufcode verursachen, der auf Unterbrechungen angewiesen ist. Jeder, der es löschen musste, konnte dies immer noch manuell tun, aber dann wäre es eine explizite Aktion, die viel wahrscheinlicher ist als ein normalerweise unbeabsichtigter Nebeneffekt, die überprüfte Exception abzufangen. So wie es jetzt aussieht, wenn Sie sich auf das unterbrochene Bit des Threads verlassen, kann jeder, der Ihren Calling-Stack, der Thread.sleep() nachlässig anruft, Ihren Tag ruinieren.

Als Ergebnis werden die meisten Java-Multi-Threaded-Code nur das Java-Thread-Interrupt-Modell mit einem "IsRunning" -Instanz-Feld und einige Mechanismen, um es als Workaround zu drehen.

Verwandte Themen