ホームページ  >  記事  >  Java  >  Java の割り込み、割り込み、isInterrupted についての理解

Java の割り込み、割り込み、isInterrupted についての理解

高洛峰
高洛峰オリジナル
2016-11-22 12:59:312618ブラウズ

今日は、Thread クラスの isInterrupted メソッドがスレッドの割り込みステータスを取得できることを確認しました:

Java の割り込み、割り込み、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,说明你能处理这个异常,你不希望上层调用者看到这个中断)。



声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。