304

Java でスレッドを適切に停止するソリューションが必要です。

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

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 を使用しています。質問は次のとおりです。

スレッドを停止して例外をスローしないようにするにはどうすればよいですか?

PS.stop();メソッドは非推奨であるため、使用したくありません。

4

8 に答える 8

187

クラスでは、クラススコープで使用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.");
        }
    }
}
于 2012-06-09T14:21:55.287 に答える
29

簡単な答え: スレッドを内部的に停止するには、次の 2 つの一般的な方法のいずれかを使用します。

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

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

  • 呼び出しsystem.exitます(これによりプロセス全体が強制終了されます)
  • スレッド オブジェクトのinterrupt()メソッドを呼び出す *
  • スレッドに、機能するように見えるメソッドが実装されているかどうかを確認します (kill()または などstop()) 。

*: これによりスレッドが停止することが期待されます。ただし、これが発生したときにスレッドが実際に何をするかは、開発者がスレッド実装を作成したときに何を書いたかに完全に依存します。

実行メソッドの実装で見られる一般的なパターンは awhile(boolean){}です。ここで、ブール値は通常、 という名前の何かでありisRunning、それはそのスレッド クラスのメンバー変数であり、揮発性であり、通常、並べ替えのセッター メソッドによって他のスレッドからアクセスできますkill() { isRunnable=false; }。これらのサブルーチンは、スレッドが保持しているリソースを終了前に解放できるため、便利です。

于 2015-04-22T21:44:29.660 に答える
10

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) {
        ...
于 2012-06-09T14:22:40.797 に答える
5

スレッドを同期する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;
        }

    }
}

もう一方のスレッドの実行を終了したい場合は、そのスレッドで countDown を実行CountDownLatchjoin、メイン スレッドに対して次のようにします。

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.");
        } 
    }
}
于 2012-06-09T14:52:01.807 に答える
3

いくつかの補足情報。Java doc では、フラグと割り込みの両方が提案されています。

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();
 }
于 2015-08-11T21:03:06.260 に答える