Frage

Ein Teamkollege machte die folgende Behauptung:

"Thread.interrupt() ist von Natur aus gebrochen und sollte (fast) niemals verwendet werden ".

Ich versuche zu verstehen, warum dies der Fall ist.

Ist es eine bekannte Best Practice, niemals zu verwenden? Thread.interrupt()? Können Sie Beweise liefern, warum es kaputt / fehlerhaft ist und nicht zum Schreiben robuster Multithread -Code verwendet werden sollte?

Notiz - Ich interessiere mich nicht für diese Frage, wenn sie von einem Design -Konservierungsmittel "hübsch" ist. Meine Frage ist - ist es Buggy?

War es hilfreich?

Lösung

Kurzversion:

Ist es eine bekannte Best Practice, niemals Thread zu verwenden.interrupt ()?

Nein.

Können Sie Beweise liefern, warum es kaputt / buggie ist und nicht zum Schreiben robuster Multithread -Code verwendet werden sollte?

Das Gegenteil ist wahr: Es ist entscheidend für Multithread -Code.

Siehe Listing 7.7 in Java Parallelität in der Praxis zum Beispiel.

Längere Version:

Hier verwenden wir diese Methode an einem bestimmten Ort: Handling InterruptedExceptions. Das mag ein bisschen seltsam erscheinen, aber so sieht es im Code aus:

try {
    // Some code that might throw an InterruptedException.  
    // Using sleep as an example
    Thread.sleep(10000);
} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    Thread.currentThread().interrupt();
}

Dies tut zwei Dinge für uns:

  1. Es wird vermieden, die Interrupt -Ausnahme zu essen. IDE Auto-Exception-Handler bieten Ihnen immer so etwas wie ie.printStackTrace(); Und ein flottes "Todo: Etwas Nützliches muss hierher gehen!" Kommentar.
  2. Es stellt den Interrupt -Status wieder her, ohne eine geprüfte Ausnahme für diese Methode zu erzwingen. Wenn die von Ihnen implementierende Methodensignatur keine hat throws InterruptedException Klausel, dies ist Ihre andere Option, um diesen unterbrochenen Status zu verbreiten.

Ein Kommentator schlug vor, dass ich eine ungeprüfte Ausnahme verwenden sollte, "um den Thread zum Sterben zu zwingen". Dies setzt voraus, dass ich Vorkenntnisse habe, dass das Abtöten des Threads plötzlich das richtige zu tun ist. Ich tu nicht.

So zitieren Sie Brian Goetz von JCIP auf der Seite vor der oben genannten Auflistung:

Eine Aufgabe sollte nichts über die Unterbrechungsrichtlinie ihres ausführenden Threads übernehmen, es sei denn, er ist ausdrücklich darauf ausgelegt, innerhalb eines Dienstes mit einer spezifischen Unterbrechungsrichtlinie ausgeführt zu werden.

Stellen Sie sich zum Beispiel vor, dass ich dies getan habe:

} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    // The following is very rude.
    throw new RuntimeException("I think the thread should die immediately", ie);
}

Ich würde erklären, dass dieser Thread unabhängig von anderen Verpflichtungen des restlichen Anrufstacks und des zugehörigen Zustands jetzt sterben muss. Ich würde versuchen, an allen anderen Fangblöcken und dem State-Reinigungscode vorbei zu schleichen, um den Tod direkt zu fädeln. Schlimmer noch, ich hätte den unterbrochenen Status des Threads konsumiert. Die vorgelagerte Logik müsste jetzt meine Ausnahme dekonstruieren, um zu versuchen, herauszufinden, ob es einen Programmlogikfehler gab oder ob ich versuche, eine geprüfte Ausnahme in einem verdeckten Wrapper zu verbergen.

Hier ist zum Beispiel, was alle anderen im Team sofort tun müssten:

try {
    callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
    if (e.getCause() instanceOf InterruptedException) {
        // Man, what is that guy's problem?
        interruptCleanlyAndPreserveState();
        // Restoring the interrupt status
        Thread.currentThread().interrupt();
    }
}

Der unterbrochene Zustand ist wichtiger als alle spezifischen InterruptException. Für ein bestimmtes Beispiel warum siehe Javadoc für Thread.interrupt ():

Wenn dieser Thread in einem Aufruf der Wait (), Wait (lang) oder Wartezeit (lang, int) Methoden der Objektklasse oder des Join () (Long), Join (Long, Int) blockiert ist (lang, int) , Schlaf (lang) oder Schlaf (lang, int), Methoden dieser Klasse, dann wird sein Interruptstatus gelöscht und erhält eine InterruptedException.

Wie Sie sehen können, kann mehr als eine InterruptedException erstellt und behandelt werden, wenn Interrupt -Anfragen verarbeitet werden, jedoch nur, wenn dieser Interrupt -Status erhalten bleibt.

Andere Tipps

Der einzige Weg, den mir bewusst bin, in dem Thread.interrupt() ist gebrochen ist, dass es nicht wirklich tut, was es so aussieht - es kann nur tatsächlich unterbrechen Code, der darauf hört.

Es scheint mir jedoch ein guter integrierter Mechanismus für die Aufgabenverwaltung und Stornierung zu sein.

Ich empfehle Java Parallelität in der Praxis Weitere Lektüre über die richtige und sichere Verwendung.

Das Hauptproblem mit Thread.interrupt() Ist die meisten Programmierer nichts über die versteckten Fallstricke und verwenden sie falsch. Wenn Sie beispielsweise mit dem Interrupt umgehen, gibt es Methoden, die klar Die Flagge (so geht der Status verloren).

Außerdem unterbricht der Anruf den Thread nicht immer sofort. Wenn es beispielsweise in einer Systemroutine hängt, wird nichts passieren. Wenn der Thread das Flag nicht überprüft und niemals eine Java -Methode aufruft, die wirft InterruptException, Das Unterbrechen hat dann keinerlei Auswirkungen.

Nein, es ist nicht buggy. Es ist tatsächlich die Grundlage dafür, wie Sie Threads in Java stoppen. Es wird im Executor -Framework von java.util.concurrent verwendet - siehe die Implementierung von java.util.concurrent.FutureTask.Sync.innerCancel.

Was das Scheitern betrifft, ich habe es nie gescheitert und habe es ausgiebig verwendet.

Ein nicht erwähnter Grund ist, dass das Interrupt -Signal verloren gehen kann, wodurch das Aufrufen des Threads.Interrupt () -Methode bedeutungslos ist. Sofern Ihr Code nicht im Thread.run () -Methode in einer Weile schleifen, ist das Ergebnis des Aufrufens von Thread.interrupt () ungewiss.

Ich habe das im Thread bemerkt ONE Ich führe aus DriverManager.getConnection() Wenn keine Datenbankverbindung verfügbar ist (sagen Sie, der Server ist somit schließlich diese Zeile ausgelöst SQLException) und aus dem Thread TWO Ich rufe explizit an ONE.interrupt(), dann beides ONE.interrupted() und ONE.isInterrupted() Rückkehr false Auch wenn als erste Zeile in der platziert catch{} Block wo SQLException wird gehandhabt.

Natürlich habe ich dieses Problem bei der Implementierung des zusätzlichen Semaphors gearbeitet, aber es ist ziemlich problematisch, da es das allererste Problem in meiner 15 -jährigen Java -Entwicklung ist.

Ich nehme an, es liegt an einem Fehler in com.microsoft.sqlserver.jdbc.SQLServerDriver. Und ich untersuchte mehr, um zu bestätigen, dass der Anruf an die native Die Funktion verbraucht diese Ausnahme in allen Fällen, in denen sie ihre eigene betrifft, aber sie bewahrt sie, wenn sie erfolgreich sind.

Tomek

PS Ich habe das gefunden Analoges Problem.

PPS Ich beilie ein sehr kurzes Beispiel für das, was ich oben schreie. Die registrierte Klasse kann in gefunden werden sqljdbc42.jar. Ich fand diesen Fehler in Klassen, die am 2015-08-20 aufgebaut sind, dann habe ich die neueste verfügbare Version (von 2017-01-12) aktualisiert und der Fehler gibt es immer noch.

import java.sql.*;

public class TEST implements Runnable{
    static{
        try{
//register the proper driver
           Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        }
        catch(ClassNotFoundException e){
            System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
        }
    }

    public void run() {
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().isInterrupted());
//prints true
        try{
            Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
        }
        catch (SQLException e){
            System.out.println(e.getMessage());
        }
        System.out.println(Thread.currentThread().isInterrupted());
//prints false
        System.exit(0);
    }

    public static void main(String[] args){
        (new Thread(new TEST())).start();
    }
}

Wenn Sie etwas völlig Falsches bestehen, als "foo", zum DriverManager.getConnection(), Sie erhalten die Meldung "Kein geeigneter Treiber für Foo", und der zweite Ausdruck wird immer noch wahr sein, wie man es erwarten würde. Wenn Sie jedoch die korrekt erstellte Zeichenfolge übergeben, aber beispielsweise Ihr Server ausgefallen ist oder Sie Ihre Nettoverbindung verloren haben (dies kann im Allgemeinen in der Produktionsumgebung auftreten), sehen Sie die java.net Socket Timeout -Fehlerausdruck und der Thread des Threads interrupted() Staat ist verloren.

Das Problem ist nicht, dass die Implementierung nicht fehlerhaft ist, sondern dass sich Ihr Thread in einem unbekannten Zustand befindet, wenn sie unterbrochen wird, und dies kann zu unvorhersehbarem Verhalten führen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top