Вопрос-ответ

How to properly stop the Thread in Java?

Как правильно остановить поток в Java?

Мне нужно решение для правильной остановки потока в Java.

У меня есть IndexProcessorкласс, который реализует интерфейс Runnable:

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(); метод, потому что он устарел.

Переведено автоматически
Ответ 1

Использование Thread.interrupt() - вполне приемлемый способ сделать это. На самом деле, это, вероятно, предпочтительнее флага, как было предложено выше. Причина в том, что если вы выполняете прерываемый блокирующий вызов (например, Thread.sleep или используете операции с каналом java.nio), вы действительно сможете сразу же выйти из них.

Если вы используете флаг, вам нужно дождаться завершения операции блокировки, а затем вы можете проверить свой флаг. В некоторых случаях вам все равно придется это делать, например, используя стандартные InputStream/OutputStream, которые нельзя прерывать.

В этом случае, когда поток прерывается, он не прерывает ввод-вывод, однако вы можете легко сделать это обычным образом в своем коде (и вы должны делать это в стратегических точках, где вы можете безопасно остановить и очистить)

if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}

Like I said, the main advantage to Thread.interrupt() is that you can immediately break out of interruptable calls, which you can't do with the flag approach.

Ответ 2

In the IndexProcessor class you need a way of setting a flag which informs the thread that it will need to terminate, similar to the variable run that you have used just in the class scope.

When you wish to stop the thread, you set this flag and call join() on the thread and wait for it to finish.

Make sure that the flag is thread safe by using a volatile variable or by using getter and setter methods which are synchronised with the variable being used as the flag.

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;
}
}

}
}

Then in 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.");
}
}
}
Ответ 3

Simple answer:
You can stop a thread INTERNALLY in one of two common ways:


  • The run method hits a return subroutine.

  • Run method finishes, and returns implicitly.

You can also stop threads EXTERNALLY:


  • Call system.exit (this kills your entire process)

  • Call the thread object's interrupt() method *

  • See if the thread has an implemented method that sounds like it would work (like kill() or stop())

*: The expectation is that this is supposed to stop a thread. However, what the thread actually does when this happens is entirely up to what the developer wrote when they created the thread implementation.

A common pattern you see with run method implementations is a while(boolean){}, where the boolean is typically something named isRunning, it's a member variable of its thread class, it's volatile, and typically accessible by other threads by a setter method of sorts, e.g. kill() { isRunnable=false; }. These subroutines are nice because they allow the thread to release any resources it holds before terminating.

Ответ 4

You should always end threads by checking a flag in the run() loop (if any).

Your thread should look like this:

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;
}
}

Then you can end the thread by calling thread.stopExecuting(). That way the thread is ended clean, but this takes up to 15 seconds (due to your sleep).
You can still call thread.interrupt() if it's really urgent - but the prefered way should always be checking the flag.

To avoid waiting for 15 seconds, you can split up the sleep like this:

        ...
try {
LOGGER.debug("Sleeping...");
for (int i = 0; (i < 150) && this.execute; i++) {
Thread.sleep((long) 100);
}

LOGGER.debug("Processing");
} catch (InterruptedException e) {
...
java multithreading