[マルチスレッド]-スレッドを適切に停止する方法

1.いつスレッドを中断する必要がありますか

実際の開発では、次のように、実行中のスレッドを中断する必要がある多くのシナリオがあります。

  1. チケット取得ソフトウェアを使用する場合、いずれかのチャネルがすでに列車のチケットを取得しています。この時点で、他のスレッドに通知して動作を停止する必要があります。
  2. 限られた時間内にタスクの結果を取得したい場合は、タスクがタイムアウトしたときにも閉じる必要があります

しかし、Javaは、コードブロックの中断のように、実行中のスレッドを単純に終了することはできません。Java自体が提供するAPIメソッドは、常に不十分だと感じます。ニーズを完全に解決する方法はありません。では、スレッドを適切に停止するにはどうすればよいでしょうか。

2、スレッドを終了する方法

1)暴力をやめる

1.stopメソッドを使用してスレッドを停止します

Javaは、スレッドの実行を停止するstopメソッドを提供します。最初に、stopメソッドがスレッドを終了する方法を示すコードを記述します。

package xiao.thread.stop;

public class StopThread {
    
    
	private static int count;

	public static void main(String[] args) throws InterruptedException {
    
    
		// first :create a thread task
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				++count;
				// 为了减少打印数 增加个计数判断 
				//也可调用sleep方法进行休眠
				if (count % 100 == 0) {
    
    
					System.out.println("此时线程依旧存活" + count);
				}
			}
		}, "thread_task for stopThread");
		thread.start();
		// 如果直接终止 很有可能thread的run方法还没有开始执行 所以建议让main线程休眠一段时间来观察效果
		Thread.currentThread().sleep(1_000);
		thread.stop();
	}
}

まず、stopと呼ぶと、スレッドが激しく停止していることがはっきりとわかります。この方法は単純ですが、Java言語自体はそうすることを推奨していません。stop()メソッドは取り消し線状態にあり、このメソッドが非推奨になったことを示しています。

2.stopメソッドを使用して提唱してみませんか

実際、スレッドの最適な状態の終了は自殺することしかできず、殺すことはできないため、stopメソッドが推奨されない理由は単純です。具体的には、他のスレッドを介してstopメソッドを呼び出すと、現時点で強制終了されたスレッドがどこで実行されたかがわかりません。たとえば、コレクションにデータを追加する操作を実行していますが、現時点ではデータがどこに追加されているかわかりません。stopメソッドを呼び出すと、強制終了されたスレッドはそれ自体が保持しているロックすぐに解放し、他のスレッドはこの時点で未処理のデータを見ることができるため、スレッドセーフの問題が発生し、オブジェクトの整合性が失われます。

2)キャッチ例外メソッド

1.割り込みメカニズム

割り込みはよく理解されており、それ自体を割り込みする機能はありませんが、単なる協調メカニズムです。Javaでは、割り込みの文法的な実装はなく、既存の割り込みフラグを使用して、割り込みの目的を達成するためのビジネスプロセスを記憶することしかできません。

2.関連API

ここに画像の説明を挿入
割り込み関数を実装する場合、一般的に使用されるAPIは主に次の3つの方法です。

1.
でこのメソッドを呼び出します。public void割り込みがtrueにスレッドの中断フラグを設定します
。2.
により、このメソッドを呼び出すパブリックブールisInterruptは、現時点で入手可能です中断フラグを返します
。3.公共の静的なブールはinterruped
このメソッドは、唯一可能Thread.interrupted()によって呼び出され、彼は2つの操作を実行します。
最初のステップは現在のスレッドの割り込みステータスに戻ることです
。2番目のステップは現在の割り込みフラグをfalse設定することです。

3.割り込みにスレッドを停止する機能がないことを確認します

まず、割り込みメソッドを呼び出すときに、jvmがターゲットスレッドに割り込むことができないことを証明する小さな例を示しましょう。

public class InterruptThread {
    
    

	private static int count;

	public static void main(String[] args) {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				System.out.println("线程依旧存活:_此时计数器状态为_" + ++count);
				try {
    
    
					Thread.currentThread().sleep(1_000);
				} catch (InterruptedException e) {
    
    
				}

			}
		},"测试线程");
		thread.start();
		thread.interrupt();
	}

}

私たちは、出力を見て:
ここに画像の説明を挿入
私たちはメソッドが割り込みを呼び出した後すぐに呼び出され始めるが、それでもコンソールにカウンタ状態を印刷毎秒のスレッドをテストする目的、およびこの例を実際の割り込みスレッドは、我々がそれを示しているがときに我々割り込みを呼び出すと、JVMはターゲットスレッドをすぐに停止しません。割り込みを呼び出した後にスレッドを停止したい場合はどうすればよいですか?

4.スレッドを停止するための例外処理方法

JVMがスレッドをアクティブに中断しないことを確認したので、interruptメソッドに記載されている割り込みフラグを使用して何かを行う必要があります。まず、新しい議論を証明する必要があります。

ブロッキングメソッドが割り込み信号を受信すると、InterruptedExceptionがスローされます

次に、上記のコードを変更します。

public class InterruptThread {
    
    
	private static int count;
	public static void main(String[] args) throws InterruptedException {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				System.out.println(Thread.currentThread().getName()+"正在运行~");
				try {
    
    
					Thread.currentThread().sleep(2_500);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
					System.out.println(Thread.currentThread().getName()+"接收到中断异常");
				}
			}

		}, "测试线程");
		thread.start();
		Thread.currentThread().sleep(1_000);
		thread.interrupt();
	}
}

操作結果:
ここに画像の説明を挿入
ここでは、割り込みメソッドを呼び出すと、この時点でスレッドをブロックする可能性のあるsleepメソッドもスレッドが呼び出すため、ターゲットスレッドが割り込み信号を受信するとinterruptExceptionをスローすることがわかります。 whileメソッド本体で実行されるコードはまだありますが、現時点ではスレッドは終了していません。この時点で、キャッチした例外を使用してコードを変換する必要があります。

public class InterruptThread {
    
    
	private static int count;
	private static boolean mark = true ;
	public static void main(String[] args) throws InterruptedException {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (mark) {
    
    
				System.out.println(Thread.currentThread().getName()+"正在运行~");
				try {
    
    
					Thread.currentThread().sleep(2_500);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
					mark = false ;
					System.err.println(Thread.currentThread().getName()+"接收到中断信号");
				}
			}
		}, "测试线程");
		thread.start();
		Thread.currentThread().sleep(1_000);
		thread.interrupt();
	}
}

動作結果:
ここに画像の説明を挿入
割り込み信号を受信した後、プログラムが実行を継続せず、スレッドが停止していることがわかります。上記のコードの変更は、実際にはwhileの判定条件で発生します。ブール変数マークを追加し、例外をキャッチしてスレッドを停止する原理は実際には非常に単純です。割り込み変数を設定し、値をに設定すると思います。実行するコード例外をキャッチすると、割り込み変数の値を変更して、ループから飛び出してスレッドを停止するという目的を達成します。
もちろん、ループから飛び出すには多くの方法があります。また、returnキーワードを使用して、ループから飛び出す効果を実現することもできます。

public class InterruptThread {
    
    
	public static void main(String[] args) throws InterruptedException {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				System.out.println(Thread.currentThread().getName()+"正在运行~");
				try {
    
    
					Thread.currentThread().sleep(2_500);
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
					System.err.println(Thread.currentThread().getName()+"接收到中断信号");
					return ;
				}
			}
		}, "测试线程");
		thread.start();
		Thread.currentThread().sleep(1_000);
		thread.interrupt();
	}
}

操作結果:
ここに画像の説明を挿入
例外をうまく利用して、returnを介してスレッドの操作を終了することもできます。

もちろん、interruptExceptionについてはまだ質問があります。

実際の開発では、例外をスローしてスレッドを終了することはできません。ログを記録するだけの場合は、プログラムが誤って中断されないように、この時点で割り込みフラグをfalseにリセットする必要があります。

3)デーモンスレッドによって実現されます(理解してください)

デーモンスレッドの特性を使用して、スレッドの操作を終了することもできます

デーモンスレッドの特性がわからない場合、前回の記事でスレッドの紹介を読むことができます。

最初にコードの実装を見てみましょう。

public class DaemonThreadInterrupt {
    
    
	public static Thread thread;
	public boolean flag = true;

	public void execut(Runnable task) {
    
    
		thread = new Thread(()->{
    
    
			Thread thread2 = new Thread(task,"任务线程");
			thread2.setDaemon(true);
			thread2.start();
			try {
    
    
				thread2.join();
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"执行线程");
		thread.start();

	}

	public void stop() {
    
    
		thread.interrupt();
	}
	
	public static void main(String[] args) {
    
    
		DaemonThreadInterrupt daemonThreadInterrupt = new DaemonThreadInterrupt();
		daemonThreadInterrupt.execut(()->{
    
    
			while(true) {
    
    
				System.out.println("程序运行");
				try {
    
    
					thread.sleep(1_000);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		daemonThreadInterrupt.stop();
	}
}

実装の原則:
上記のコードでは、実装が必要なタスクスレッドを実行スレッドに配置しました。実行スレッドはビジネスロジックの処理を担当せず、タスクスレッドの開始のみを担当します。タスクスレッドが開始されると、実行スレッドのデーモンスレッドとして設定されます。次に、彼を実行のスレッドに参加させます。
次に、joinメソッドの特性とデーモンスレッドの特性を使用してstopメソッドを設計しました。joinメソッドの
実行後、実行スレッドはタスクスレッドの実行を待ってから実行しますが、割り込みメソッドを呼び出すと、joinメソッドは受信します。割り込み信号に達すると、例外がスローされ、実行スレッドはタスクスレッドの実行を待機しなくなります。ここでポイントがあります。この時点で、実行スレッドは終了し、実行スレッドのデーモンスレッドとしてのタスクスレッドもそのライフサイクルを終了します。
この方法は、スレッドの複数の特性を駆使してスレッドを停止し、終了プロセスを制御できるほか、タイミングタスクスレッドの実現に適した遅延終了を設定することもできます。

おすすめ

転載: blog.csdn.net/xiaoai1994/article/details/110041270