Javaでスレッドを適切に停止するための解決策が必要です。
Runnableインターフェースを実装するIndexProcessor
classがあります。
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
@Override
public void run() {
boolean run = true;
while (run) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
}
そして私はServletContextListener
クラスを使ってスレッドを開始したり停止したりします。
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
@Override
public void contextInitialized(ServletContextEvent event) {
thread = new Thread(new IndexProcessor());
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
thread.interrupt();
LOGGER.debug("Thread successfully stopped.");
}
}
}
しかし、Tomcatをシャットダウンすると、IndexProcessorクラスで例外が発生します。
2012-06-09 17:04:50,671 [Thread-3] ERROR IndexProcessor Exception
Java.lang.InterruptedException: sleep interrupted
at Java.lang.Thread.sleep(Native Method)
at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.Java:22)
at Java.lang.Thread.run(Unknown Source)
JDK 1.6を使用しています。それで問題は次のとおりです。
スレッドを停止して例外をスローしないようにするにはどうすればよいですか。
P.S .stop();
メソッドは推奨されていないので、使用したくありません。
IndexProcessor
クラスでは、クラススコープ内で使用した変数run
と同様に、スレッドに終了する必要があることを通知するフラグを設定する方法が必要です。
スレッドを停止したい場合は、このフラグを設定してスレッド上でjoin()
を呼び出し、それが完了するのを待ちます。
Volatile変数を使用するか、フラグとして使用されている変数と同期するgetterメソッドおよびsetterメソッドを使用して、フラグがスレッドセーフであることを確認してください。
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean running = true;
public void terminate() {
running = false;
}
@Override
public void run() {
while (running) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
}
}
それからSearchEngineContextListener
で:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
private IndexProcessor runnable = null;
@Override
public void contextInitialized(ServletContextEvent event) {
runnable = new IndexProcessor();
thread = new Thread(runnable);
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
runnable.terminate();
thread.join();
LOGGER.debug("Thread successfully stopped.");
}
}
}
Thread.interrupt()
を使うことはこれをするのに完全に受け入れられる方法です。実際には、上記のようにおそらくフラグよりも好まれます。その理由は、Thread.sleep
やJava.nio Channelオペレーションの使用のように、割り込み可能なブロッキング呼び出しを行っている場合、実際にすぐにそれらから抜け出すことができるでしょう。
あなたが旗を使うならば、あなたはブロッキング操作が終わるのを待たなければなりません、そしてあなたはあなたの旗をチェックすることができます。割り込み不可能な標準のInputStream
/OutputStream
を使うなど、場合によってはこれをやらなければなりません。
その場合、スレッドが中断されてもIOは中断されませんが、コード内でこれを日常的に簡単に行うことができます(安全に停止してクリーンアップできる戦略的な時点でこれを行うべきです)
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}
私が言ったように、Thread.interrupt()
の主な利点はあなたが即座に割り込み可能な呼び出しから抜け出すことができるということです、あなたはフラグアプローチでそれをすることができません。
簡単な答え:2つの一般的な方法の1つでスレッドを内部的に停止することができます。
スレッドを外部で停止することもできます。
system.exit
を呼び出します(これによりプロセス全体が強制終了されます)。interrupt()
メソッドを呼び出す*kill()
やstop()
のように)*:これはスレッドを停止させることになっていることを期待しています。ただし、これが発生したときにスレッドが実際に行うことは、開発者がスレッドの実装を作成したときに書いたことに完全に依存します。
Runメソッドの実装でよく見られるパターンはwhile(boolean){}
です。ここで、booleanは通常isRunning
という名前のもので、そのスレッドクラスのメンバー変数です。 kill() { isRunnable=false; }
。これらのサブルーチンは、終了する前にスレッドが保持しているリソースを解放することをスレッドに許可するので、Niceです。
run()
ループ内のフラグをチェックして(存在する場合)、常にスレッドを終了させる必要があります。
あなたのスレッドは次のようになります。
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean execute;
@Override
public void run() {
this.execute = true;
while (this.execute) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
this.execute = false;
}
}
}
public void stopExecuting() {
this.execute = false;
}
}
その後、thread.stopExecuting()
を呼び出してスレッドを終了できます。そのようにしてスレッドはクリーンに終了しますが、これは最大15秒かかります(あなたの睡眠のため)。それが本当に緊急であるなら、あなたはまだthread.interrupt()を呼び出すことができます - しかし好ましい方法は常に旗をチェックするべきです。
15秒間待つのを避けるために、このように睡眠を分割することができます。
...
try {
LOGGER.debug("Sleeping...");
for (int i = 0; (i < 150) && this.execute; i++) {
Thread.sleep((long) 100);
}
LOGGER.debug("Processing");
} catch (InterruptedException e) {
...
JDK 1.0を使用している場合は、Threadの廃止予定のメソッドstop()を呼び出して終了させることができます。 stop()を使用するのは非常に危険です。たとえそれが重要な何かの最中にあってもそれはあなたのスレッドを殺すでしょうから。自分自身を保護する方法はありません。そのため、stop()を使用するコードを見つけた場合は、眉をひそめてください。
スレッドをクリーンにシャットダウンする方法
Javaでは、スレッドの開始は簡単ですが、停止するには多くの注意と努力が必要です。
これがJavaでどのように設計されているかです。外部から設定できるすべてのJavaスレッド、つまり親スレッドまたはメインスレッドには、割り込みステータスフラグと呼ばれるフラグがあります。そして、スレッドは時々それをチェックし、その実行を停止します。自発的に..!方法は次のとおりです。
Thread loop = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (Thread.interrupted()) {
break;
}
// Continue to do nothing
}
}}); loop.start(); loop.interrupt();
source: Javaでスレッドを止める方法マルチスレッド
スレッドを同期させるには、実行中のプロセスが完了するまでスレッドが待機するのに役立つCountDownLatch
を使用することをお勧めします。この場合、ワーカークラスは指定された数のCountDownLatch
インスタンスで設定されます。 await
メソッドの呼び出しにより、現在のカウントがゼロに達するか、タイムアウト設定に達するまで、countDown
メソッドの呼び出しはブロックされます。この方法では、指定された待機時間が経過するのを待たずにスレッドを即座に中断できます。
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private final CountDownLatch countdownlatch;
public IndexProcessor(CountDownLatch countdownlatch) {
this.countdownlatch = countdownlatch;
}
public void run() {
try {
while (!countdownlatch.await(15000, TimeUnit.MILLISECONDS)) {
LOGGER.debug("Processing...");
}
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
他のスレッドの実行を終了したいときは、メインスレッドに対してCountDownLatch
およびjoin
スレッドでcountDownを実行します。
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
private IndexProcessor runnable = null;
private CountDownLatch countdownLatch = null;
@Override
public void contextInitialized(ServletContextEvent event) {
countdownLatch = new CountDownLatch(1);
Thread thread = new Thread(new IndexProcessor(countdownLatch));
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (countdownLatch != null)
{
countdownLatch.countDown();
}
if (thread != null) {
try {
thread.join();
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
}
LOGGER.debug("Thread successfully stopped.");
}
}
}
いくつかの補足情報Javaドキュメントではflagとinterruptの両方が推奨されています。
https://docs.Oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
private volatile Thread blinker;
public void stop() {
blinker = null;
}
public void run() {
Thread thisThread = Thread.currentThread();
while (blinker == thisThread) {
try {
Thread.sleep(interval);
} catch (InterruptedException e){
}
repaint();
}
}
長期間(たとえば入力のために)待機するスレッドの場合は、Thread.interrupt
を使用します。
public void stop() {
Thread moribund = waiter;
waiter = null;
moribund.interrupt();
}
私はAndroidの中で動作するように割り込みを取得できませんでしたので、私はこの方法を使用して、完璧に動作します:
boolean shouldCheckUpdates = true;
private void startupCheckForUpdatesEveryFewSeconds() {
threadCheckChat = new Thread(new CheckUpdates());
threadCheckChat.start();
}
private class CheckUpdates implements Runnable{
public void run() {
while (shouldCheckUpdates){
System.out.println("Do your thing here");
}
}
}
public void stop(){
shouldCheckUpdates = false;
}
通常、スレッドは中断されると終了します。それでは、なぜネイティブブール値を使用しないのですか? isInterrupted()を試してください。
Thread t = new Thread(new Runnable(){
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
// do stuff
}
}});
t.start();
// Sleep a second, and then interrupt
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
t.interrupt();