Heute habe ich gesehen, dass die isInterrupted-Methode der Thread-Klasse den Interrupt-Status des Threads ermitteln kann:
Also habe ich ein Beispiel geschrieben, um es zu überprüfen:
public class Interrupt { public static void main(String[] args) throws Exception { Thread t = new Thread(new Worker()); t.start(); Thread.sleep(200); t.interrupt(); System.out.println("Main thread stopped."); } public static class Worker implements Runnable { public void run() { System.out.println("Worker started."); try { Thread.sleep(500); } catch (InterruptedException e) { System.out.println("Worker IsInterrupted: " + Thread.currentThread().isInterrupted()); } System.out.println("Worker stopped."); } } }
Der Inhalt ist sehr einfach: Der Hauptthread main startet einen Sub-Thread-Worker und lässt den Worker dann 500 ms lang und den Hauptthread 200 ms lang schlafen. Danach ruft main die Interrupt-Methode des Worker-Threads auf um den Worker zu unterbrechen Nachdem der Worker unterbrochen wurde, wird der Unterbrechungsstatus gedruckt. Das Folgende ist das Ausführungsergebnis:
Worker started. Main thread stopped. Worker IsInterrupted: falseWorker stopped.
Worker wurde offensichtlich unterbrochen, aber die Methode isInterrupted() hat false zurückgegeben. Warum?
Nachdem ich mich auf Stackoverflow umgesehen habe, habe ich festgestellt, dass einige Internetnutzer erwähnt haben, dass Sie das JavaDoc (oder den Quellcode) der Methode anzeigen können, die InterruptedException auslöst. Daher habe ich die Dokumentation der Thread.sleep-Methode überprüft was das Dokument sagt. Beschreibung dieser InterruptedException-Ausnahme:
InterruptedException – wenn ein Thread den aktuellen Thread unterbrochen hat, wird der unterbrochene Status des aktuellen Threads gelöscht, wenn diese Ausnahme ausgelöst wird.
Beachten Sie das Folgender Satz: „Wenn diese Ausnahme ausgelöst wird, wurde der Interrupt-Status gelöscht.“ Daher sollte die Methode isInterrupted() false zurückgeben. Aber manchmal brauchen wir die isInterrupted-Methode, um true zurückzugeben. Was sollen wir tun? Hier werden wir zunächst über den Unterschied zwischen Interrupt, Interrupted und IsInterrupted sprechen:
Die Interrupt-Methode wird zum Unterbrechen von Threads verwendet, und der Status des Threads, der diese Methode aufruft, wird auf den Status „Unterbrochen“ gesetzt. Hinweis: Thread-Interrupt setzt nur das Interrupt-Statusbit des Threads und stoppt den Thread nicht. Der Benutzer muss den Status des Threads überwachen und ihn selbst bearbeiten. Die Methode, die Thread-Unterbrechungen unterstützt (d. h. die Methode, die InterruptedException auslöst, nachdem der Thread unterbrochen wurde, wie z. B. Sleep hier und Object.wait und andere Methoden), dient dazu, den Unterbrechungsstatus des Threads zu überwachen Wenn der Thread auf „Unterbrochener Status“ eingestellt ist, wird eine Interrupt-Ausnahme ausgelöst. Diese Ansicht kann durch diesen Artikel bestätigt werden:
interrupt() legt lediglich den Unterbrechungsstatus des Threads fest. Code, der im unterbrochenen Thread ausgeführt wird, kann später den unterbrochenen Status abfragen, um zu sehen, ob er aufgefordert wurde, seine Arbeit zu stoppen
Werfen wir einen Blick auf die Implementierung der unterbrochenen Methode:
public static boolean interrupted() { return currentThread().isInterrupted(true); }
und die Implementierung von isInterrupted:
public boolean isInterrupted() { return isInterrupted(false); }
Eine dieser beiden Methoden ist statisch und Das andere ist nicht der Fall, aber tatsächlich rufen sie alle dieselbe Methode auf, mit der Ausnahme, dass der von der unterbrochenen Methode übergebene Parameter wahr ist und der von inInterrupted übergebene Parameter falsch ist. Was bedeutet dieser Parameter? Schauen wir uns die Implementierung der isInterrupted(boolean)-Methode an:
/** * Tests if some Thread has been interrupted. The interrupted state * is reset or not based on the value of ClearInterrupted that is * passed. */private native boolean isInterrupted(boolean ClearInterrupted);
Dies ist eine native Methode, wenn Sie den Parameternamen ClearInterrupted nicht sehen können Die Rolle dieses Parameters wird deutlich ausgedrückt: Ob der Interrupt-Status gelöscht werden soll. In der Annotation der Methode heißt es auch deutlich: „Der Interrupt-Status wird basierend auf dem übergebenen ClearInterrupted-Parameterwert zurückgesetzt.“ Daher löscht die statische Methode interrupted den Interrupt-Status (der übergebene Parameter ClearInterrupted ist wahr), die Instanzmethode isInterrupted jedoch nicht (der übergebene Parameter ClearInterrupted ist falsch).
Zurück zur Frage gerade: Wenn Sie möchten, dass die Methode isInterrupted true zurückgibt, können Sie den unterbrochenen Zustand natürlich wiederherstellen, indem Sie die Methode interrupt() erneut aufrufen, bevor Sie die Methode isInterrupted aufrufen:
public class Interrupt { public static void main(String[] args) throws Exception { Thread t = new Thread(new Worker()); t.start(); Thread.sleep(200); t.interrupt(); System.out.println("Main thread stopped."); } public static class Worker implements Runnable { public void run() { System.out.println("Worker started."); try { Thread.sleep(500); } catch (InterruptedException e) { Thread curr = Thread.currentThread(); //再次调用interrupt方法中断自己,将中断状态设置为“中断” curr.interrupt(); System.out.println("Worker IsInterrupted: " + curr.isInterrupted()); System.out.println("Worker IsInterrupted: " + curr.isInterrupted()); System.out.println("Static Call: " + Thread.interrupted());//clear status System.out.println("---------After Interrupt Status Cleared----------"); System.out.println("Static Call: " + Thread.interrupted()); System.out.println("Worker IsInterrupted: " + curr.isInterrupted()); System.out.println("Worker IsInterrupted: " + curr.isInterrupted()); } System.out.println("Worker stopped."); } } }
Ausführungsergebnisse:
Worker started. Main thread stopped. Worker IsInterrupted: true Worker IsInterrupted: true Static Call: true ---------After Interrupt Status Cleared---------- Static Call: false Worker IsInterrupted: false Worker IsInterrupted: false Worker stopped.
Sie können auch aus den Ausführungsergebnissen ersehen, dass die isInterrupted-Methode war Die ersten beiden Aufrufe geben beide true zurück, was darauf hinweist, dass die isInterrupted-Methode den Interrupt-Status des Threads nicht ändert, und rufen dann die statische interrupted()-Methode auf Mal wird false zurückgegeben, da beim ersten Aufruf der Interrupt-Status gelöscht wurde. Die letzten beiden Aufrufe der Methode isInterrupted() werden definitiv false zurückgeben.
In welchem Szenario müssen wir also den Thread im Catch-Block unterbrechen (den Interrupt-Status zurücksetzen)?
Die Antwort lautet: Wenn InterruptedException nicht ausgelöst werden kann (genau wie die Thread.sleep-Anweisung hier in der Run-Methode von Runnable platziert ist, lässt diese Methode das Auslösen von geprüften Ausnahmen nicht zu), Sie möchten es aber mitteilen obere Ebene Wenn beim Anrufer eine Unterbrechung auftritt, kann der Unterbrechungsstatus nur in Catch zurückgesetzt werden.
Wenn Sie eine InterruptedException abfangen, sie aber nicht erneut auslösen können, sollten Sie Beweise dafür aufbewahren, dass die Unterbrechung aufgetreten ist, damit Code weiter oben im Aufrufstapel von der Unterbrechung erfahren und darauf reagieren kann, wenn er dies wünscht durch Aufrufen von interrupt(), um den aktuellen Thread „erneut zu unterbrechen“, wie in Listing 3 gezeigt.
Listing 3: Wiederherstellen des unterbrochenen Status nach dem Abfangen von InterruptedException
public class TaskRunner implements Runnable { private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } public void run() { try { while (true) { Task task = queue.take(10, TimeUnit.SECONDS); task.execute(); } } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }
那么问题来了:为什么要在抛出InterruptedException的时候清除掉中断状态呢?
这个问题没有找到官方的解释,估计只有Java设计者们才能回答了。但这里的解释似乎比较合理:一个中断应该只被处理一次(你catch了这个InterruptedException,说明你能处理这个异常,你不希望上层调用者看到这个中断)。