今日は、Thread クラスの isInterrupted メソッドがスレッドの割り込みステータスを取得できることを確認しました:
そこで、それを検証するための例を書きました:
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."); } } }
内容は非常に単純な答えです: メインスレッド main が開始しますサブスレッド ワーカーを作成し、ワーカーを 500 ミリ秒スリープさせ、メインを 200 ミリ秒スリープさせます。その後、メインはワーカー スレッドの割り込みメソッドを呼び出してワーカーに割り込みます。ワーカーが割り込まれた後、割り込みステータスが出力されます。以下は実行結果です:
Worker started. Main thread stopped. Worker IsInterrupted: falseWorker stopped.
Worker は明らかに中断されましたが、isInterrupted() メソッドは false を返しました。なぜでしょうか。
stackoverflow を検索したところ、InterruptedException をスローするメソッドの JavaDoc (またはソース コード) を参照できると一部のネットユーザーが言及していることがわかりました。そのため、Thread.sleep メソッドのドキュメントを確認しました。このドキュメントには、この InterruptedException 例外が説明されています。このように:
InterruptedException - スレッドが現在のスレッドに割り込んだ場合、この例外がスローされると、現在のスレッドの割り込みステータスがクリアされます。
次の文に注目してください。「この例外がスローされると、割り込みステータスはクリアされます」クリア"。したがって、isInterrupted() メソッドは false を返す必要があります。しかし、場合によっては、isInterrupted メソッドが true を返す必要がある場合があります。ここではまず、interrupt、interrupted、isInterrupted の違いについて説明します。
interrupt メソッドはスレッドを中断するために使用され、このメソッドを呼び出しているスレッドのステータスは「中断」ステータスに設定されます。注: スレッド割り込みは、スレッドの割り込みステータス ビットを設定するだけであり、スレッドは停止しません。ユーザーはスレッドのステータスを監視し、自分で処理する必要があります。スレッドの中断をサポートするメソッド (つまり、ここで言う sleep や Object.wait などのスレッドが中断された後に InterruptedException をスローするメソッド) は、スレッドの中断状況を一度監視します。スレッドが「割り込みステータス」に設定されている場合、割り込み例外がスローされます。この見解は、次の記事で確認できます:
interrupt() は、スレッドの割り込みステータスを設定するだけです。割り込まれたスレッドで実行されているコードは、後で割り込みステータスをポーリングして、実行内容の停止が要求されているかどうかを確認できます
もう一度参照してください。中断されたメソッドの実装:
public static boolean interrupted() { return currentThread().isInterrupted(true); }
と isInterrupted の実装:
public boolean isInterrupted() { return isInterrupted(false); }
これら 2 つのメソッドの 1 つは静的で、もう 1 つは静的ではありませんが、実際には同じメソッドを呼び出しています。中断されたメソッドは true です。inInterrupted に渡されるパラメータは false です。では、このパラメータは何を意味するのでしょうか? isInterrupted(boolean) メソッドの実装を見てみましょう:
/** * 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);
これは、ソース コードが見えなくても問題ありません。パラメータ名 ClearInterrupted は、このメソッドの機能を明確に表しています。パラメータ - 割り込みステータスをクリアするかどうか。メソッドのアノテーションでも、「渡された ClearInterrupted パラメータ値に基づいて割り込みステータスがリセットされる」ことが明確に表現されています。したがって、静的メソッド Interrupted は割り込みステータスをクリアします (渡されたパラメータ ClearInterrupted が true) が、インスタンス メソッド isInterrupted はクリアしません (渡されたパラメータ ClearInterrupted が false)。
前の質問に戻る: 明らかに、isInterrupted メソッドに true を返したい場合は、isInterrupted メソッドを呼び出す前に再度中断() メソッドを呼び出すことで、中断された状態を復元できます:
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."); } } }
実行結果:
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.
また、実行結果から、 isInterrupted メソッドへの最初の 2 つの呼び出しが true を返し、isInterrupted メソッドがスレッドの割り込みステータスを変更しないことを示し、その後、静的中断() メソッドが実行されることもわかります。最初の呼び出しではスレッドが中断されたことを示す true が返され、最初の呼び出しで中断ステータスがクリアされたため、2 回目の呼び出しでは false が返されます。 isInterrupted() メソッドへの最後の 2 つの呼び出しは、必ず false を返します。
それでは、どのようなシナリオで catch ブロックでスレッドを中断する (割り込みステータスをリセットする) 必要があるのでしょうか?
答えは次のとおりです。InterruptedException をスローできない場合 (ここでの Thread.sleep ステートメントが Runnable の run メソッドに配置されているのと同じように、このメソッドはチェック例外のスローを許可しません)、上位の呼び出し元に何が起こったかを伝えたい場合です。ここで 割り込みが発生した場合、割り込みステータスはキャッチでのみリセットできます。
InterruptedException をキャッチしても再スローできない場合は、コール スタックの上位のコードが割り込みを学習し、必要に応じて応答できるように、割り込みが発生した証拠を保存する必要があります。このタスクは、interrupt(. ) リスト 3 に示すように、現在のスレッドを「再中断」します。
リスト 3: 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,说明你能处理这个异常,你不希望上层调用者看到这个中断)。