web-dev-qa-db-ja.com

Javaでスレッドを正しく停止する方法

Javaでスレッドを適切に停止するための解決策が必要です。

Runnableインターフェースを実装するIndexProcessorclassがあります。

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();メソッドは推奨されていないので、使用したくありません。

252

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.");
        }
    }
}
160
DrYap

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()の主な利点はあなたが即座に割り込み可能な呼び出しから抜け出すことができるということです、あなたはフラグアプローチでそれをすることができません。

278
Matt

簡単な答え:2つの一般的な方法の1つでスレッドを内部的に停止することができます。

  • Runメソッドはreturnサブルーチンにヒットします。
  • Runメソッドは終了し、暗黙的に戻ります。

スレッドを外部で停止することもできます。

  • system.exitを呼び出します(これによりプロセス全体が強制終了されます)。
  • スレッドオブジェクトのinterrupt()メソッドを呼び出す*
  • スレッドが動作するように思える実装済みメソッドを持っているかどうかを確認してください(kill()stop()のように)

*:これはスレッドを停止させることになっていることを期待しています。ただし、これが発生したときにスレッドが実際に行うことは、開発者がスレッドの実装を作成したときに書いたことに完全に依存します。

Runメソッドの実装でよく見られるパターンはwhile(boolean){}です。ここで、booleanは通常isRunningという名前のもので、そのスレッドクラスのメンバー変数です。 kill() { isRunnable=false; }。これらのサブルーチンは、終了する前にスレッドが保持しているリソースを解放することをスレッドに許可するので、Niceです。

24
hamsterofdark

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) {
        ...
9
Chris

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でスレッドを止める方法マルチスレッド

6
riteeka

スレッドを同期させるには、実行中のプロセスが完了するまでスレッドが待機するのに役立つ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.");
        } 
    }
}
5

いくつかの補足情報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();
 }
3
Feng

私は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;
 }
2
Mindborg

通常、スレッドは中断されると終了します。それでは、なぜネイティブブール値を使用しないのですか? 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();

ref- どうすればスレッドをkillできますか? stop()を使用せずに