目次
1. 割り込み機構
並行プログラミングでは、あるスレッドの実行が別のスレッドによって中断されることがあります。この中断は「中断」と呼ばれます。割り込みは、あるスレッドが別のスレッドに通知し、現在の作業を停止して他の操作を実行することを要求できるスレッド間通信メカニズムです。JUC の割り込みメカニズムはinterrupt()
メソッドを通じて実装されます。
1.1 割り込み原理
割り込みは、スレッドの割り込みフラグ ビットを通じて行われます。各 Java スレッドには、それに関連付けられたブール値の割り込みフラグがあります。スレッドがinterrupt()
メソッドを呼び出すと、ターゲット スレッドの割り込みフラグ ビットが に設定されtrue
、スレッドが割り込まれたことを示します。割り込みを受けたスレッドは、自身の割り込みステータスをチェックすることで割り込み要求を発見できます。
1.2 割り込み方式
1.2.1 interrupt()
方法
interrupt()
メソッドは、Thread
現在のスレッドに割り込んだり、ターゲット スレッドを指定したりするために使用されるクラスのインスタンス メソッドです。次のように宣言されています。
public void interrupt()
このメソッドを呼び出すと、interrupt()
ターゲット スレッドの割り込みフラグ ビットが に設定されますtrue
。ターゲット スレッドがブロック状態にある場合 (たとえばsleep()
、wait()
、などのメソッドを呼び出している場合)、すぐに例外をjoin()
スローし、ブロック状態から戻ります。InterruptedException
スレッドがブロック状態で中断され、InterruptedException
例外がスローされた後は、中断された状態を復元する必要があります。これは、例外がスローされた後、割り込みフラグがクリアされるためで、後続のコードで割り込みステータスを正しく検出したい場合は、手動で割り込みフラグを再度設定する必要があります。
catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
}
1.2.2 isInterrupted()
方法
isInterrupted()
メソッドは、Thread
現在のスレッドの割り込みステータスをクエリするために使用されるクラスのインスタンス メソッドです。次のように宣言されています。
public boolean isInterrupted()
呼び出したisInterrupted()
メソッドは、現在のスレッドの割り込みフラグ ビットを返します。このメソッドを呼び出しても割り込みフラグはクリアされないことに注意してください。
1.2.3 Thread.interrupted()
方法
Thread.interrupted()
このメソッドはThread
クラスの静的メソッドであり、現在のスレッドの割り込みステータスを照会し、割り込みフラグ ビットをクリアするために使用されます。次のように宣言されています。
public static boolean interrupted()
呼び出しThread.interrupted()
メソッドは、現在のスレッドの割り込みステータスを返し、現在のスレッドの割り込みフラグ ビットを にリセットしますfalse
。
1.3 割り込みを正しく処理する
スレッド実行の重要な時点で、スレッドの割り込みステータスをチェックし、状況に応じて対応する応答を行う必要があります。たとえば、ループ内でタスクを実行するときに、isInterrupted()
このメソッドを使用して割り込みステータスをチェックし、スレッドが割り込まれたことがわかった場合は、ループを終了して時間内にスレッドを終了できます。
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务...
}
}
割り込みフラグビットを他のメソッドやオブジェクトに渡すことがありますが、割り込みロジックの処理後は、後続の割り込み判定に影響を与えないように割り込みステータスをクリアする必要があります。メソッドを使用して、Thread.interrupted()
割り込みフラグビットをクリアできます。
Thread th = new Thread(() -> {
while (!Thread.interrupted()){
System.out.println(Thread.currentThread().getName());
Thread.currentThread().interrupt();
}
}, "th");
th.start();
1.4 実行中のスレッドの中断を停止する
1.4.1 揮発性
volatile
Java のキーワードで、変数を宣言するために使用されます。その主な機能は、変更された変数がすべてのスレッドに確実に表示されるようにすることです。volatile
つまり、volatile
変数が読み取られるたびに、スレッドのローカル キャッシュ値を使用するのではなく、最新の値がメイン メモリから直接取得されます。マルチスレッド環境では、スレッドが変更されvolatile
た変数の値を変更すると、その値は直ちにメイン メモリに更新され、他のスレッドに通知され、他のスレッドは変数を読み取るときに最新の値を確認します。これにより、変数への変更がすべてのスレッドに表示されるようになります。volatile
また、命令の再配置を防止し、volatile
変更された変数の読み取りおよび書き込み操作が適切に実行され、予期しない結果が発生しないようにすることもできます。
public class Test {
private static volatile boolean key=false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (true){
if(key){
System.out.println(Thread.currentThread().getName()+ "终止");
break;
}
System.out.println(Thread.currentThread().getName()+" 执行");
}
}).start();
new Thread(()->{
key=true;
}).start();
}
}
1.4.2 アトミッククラス
アトミック クラスは、アトミック操作を実装するために JUC によって提供されるツール クラスのセットであり、特定の操作のアトミック性を保証します。アトミック クラスは、CAS (比較および交換) アルゴリズムを通じてアトミック操作を実装します。CAS は、ロックを使用せずにスレッドセーフな同時操作を実装できるオプティミスティック ロック テクノロジです。
一般的に使用されるアトミック クラスにはAtomicInteger
、、、AtomicLong
などAtomicBoolean
が含まれます。
public class Test {
private static AtomicBoolean atomicBoolean=new AtomicBoolean(false);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (true){
if(atomicBoolean.get()){
System.out.println(Thread.currentThread().getName()+ "终止");
break;
}
System.out.println(Thread.currentThread().getName()+" 执行");
}
}).start();
new Thread(()->{
atomicBoolean.set(true);
}).start();
}
}
3 番目のケースは、記事の冒頭で紹介した割り込みメソッドを使用する場合です。ここでは省略します。