Threadクラスには非推奨のstop()メソッドがあり、スレッドを終了できますが、ステップ数に関係なくスレッドを直接終了するため、非推奨になりました。たとえば、スレッドが停止した後、いくつかの余波操作(外部リソースのシャットダウンなど)が必要になり、このメソッドでは何もできません。スレッドの終了は、スレッドの中断によって実現できます。
まず、Javaスレッドの中断の内容のいくつかを見てください。
-
Javaプラットフォームは、スレッドごとにブール割り込みフラグを維持し、フラグの値は次のメソッドで取得でき
ます
。interrupt()はスレッドに割り込みますisInterrupted()はスレッドの割り込みフラグを
返しますinterrupted()はスレッドを返し、リセットします割り込みフラグ(falseに設定) -
中断は、開始スレッドからターゲットスレッドへの要求のみです。つまり、ターゲットスレッドはこの要求に応答するか、無視することができます。
-
Java標準ライブラリのスレッドブロッキングに関連するメソッドはすべて、割り込みに応答してInterruptedException例外をスローします。慣例により、例外をスローする前に割り込みフラグがfalseにリセットされるため、これらのメソッドはスレッドの割り込みフラグをクリアします。
-
Java標準ライブラリのスレッドブロッキングに関連するメソッドは、ブロッキングの前に割り込みフラグがtrueかどうかを判断し、trueの場合は例外をスローします。ブロッキング後に割り込みメソッドが呼び出されると、JVMはの割り込みフラグを設定します。スレッド、次にスレッドがウェイクアップするため、割り込みはスレッドをウェイクアップする効果があります。
上記の点と太字の2番目の文から、対象のスレッドが割り込みマークを判断する限り、中断されたスレッドがブロックされた状態であっても、スレッドの中断を使用してスレッドの終了を実現できることがわかります。目覚めさせて終了する;太字の最初の文から、Java標準ライブラリの一部のブロッキングメソッドが呼び出され、割り込みフラグがクリアされ、割り込みフラグを取得できない(合計がfalse)ため、使用するには割り込みフラグを作成する必要があります。
たとえば、以下は割り込み可能なタスクエグゼキュータです。各タスクが実行される前に、自己定義されたiの終了マークと残りのタスクの数(余波)が決定されます。提供されるシャットダウンメソッドは、ワーカーの割り込みに加えて行われます。スレッド(主に機能は、ブロックされた状態にある可能性のあるタスクをウェイクアップすることです)、および終了した終了交差点をtrueに設定します。
mainメソッドを実行すると、最初に「クライアントがシャットダウンメソッドを呼び出しました」と出力され、4秒後にメインスレッドが終了します。shutdownメソッドがターゲットスレッドを正しく終了していることがわかります。「慣例に従い、Java標準ライブラリでInterruptedExceptionをスローするスレッド関連のブロッキングメソッドは割り込みフラグをクリアします」に関しては、条件で!interinatedを!Thread.currentThread()。isInterrupted()に置き換えてから、 mainメソッドテストを実行すると、sleep()メソッドが割り込みフラグをクリアするため、メインスレッドを終了できないことがわかります。そのため、!Thread.currentThread()。isInterrupted()は常にtrueであり、ワーカースレッドが使用できなくなります。終了します。
public class TerminableTaskRunner {
// 存储要执行的任务
private final BlockingQueue<Runnable> tasks;
// 线程终止标志
private volatile boolean terminated;
// 剩余的任务数
private final AtomicInteger count;
// 实际执行任务的线程
private volatile Thread workThread;
public TerminableTaskRunner(int capacity) {
this.tasks = new LinkedBlockingDeque<>(capacity);
this.count = new AtomicInteger(0);
this.workThread = new WorkThread();
workThread.start();
}
public void submit(Runnable task) {
this.tasks.add(task);
this.count.incrementAndGet();
}
public void shutdown() {
terminated = true; // 线程终止标志,由于中断标志可能会被覆盖,所以需要自己创建一个标志
if (workThread != null)
workThread.interrupt(); // 唤醒线程
}
private class WorkThread extends Thread {
@Override
public void run() {
Runnable task;
try {
while (!terminated || tasks.size() >= 1) {
task = tasks.take();
try {
task.run(); // 可能会清空当前线程的中断标记,如task.run()在内部调用的阻塞方法抛出了InterruptedException
} catch (Throwable e) {
e.printStackTrace();
}
count.decrementAndGet();
}
} catch (InterruptedException e) {
// 一旦调用shutdown且tasks.take()阻塞住,就抛出该异常,没有任务要执行,直接终止
workThread = null;
}
}
}
public static void main(String[] args) {
TerminableTaskRunner taskRunner = new TerminableTaskRunner(4);
for (int i = 0; i < 4; i++) {
taskRunner.submit(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("客户端调用了 shutdown 方法");
}
});
}
taskRunner.shutdown();
}
}