¿Cómo detener correctamente el hilo en Java?
Necesito una solución para detener correctamente el hilo en Java.
Tengo IndexProcessor
una clase que implementa la interfaz 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;
}
}
}
}
Y tengo ServletContextListener
una clase que inicia y detiene el hilo:
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.");
}
}
}
Pero cuando cierro Tomcat, aparece la excepción en mi clase 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)
Estoy usando JDK 1.6. Entonces la pregunta es:
¿Cómo puedo detener el hilo y no generar ninguna excepción?
PD: No quiero utilizar .stop();
el método porque está en desuso.
Usar Thread.interrupt()
es una forma perfectamente aceptable de hacer esto. De hecho, probablemente sea preferible a una bandera como se sugirió anteriormente. La razón es que si estás en una llamada de bloqueo interrumpible (como Thread.sleep
o usando operaciones del canal java.nio), podrás salir de ellas de inmediato.
Si usa una bandera, debe esperar a que finalice la operación de bloqueo y luego podrá verificar su bandera. En algunos casos, debe hacer esto de todos modos, como por ejemplo usando InputStream
/ estándar OutputStream
que no son interrumpibles.
En ese caso, cuando se interrumpe un subproceso, no interrumpirá la IO; sin embargo, puede hacerlo fácilmente de forma rutinaria en su código (y debe hacerlo en puntos estratégicos donde pueda detenerse y limpiarse de manera segura).
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}
Como dije, la principal ventaja Thread.interrupt()
es que puedes salir inmediatamente de las llamadas interrumpibles, lo que no puedes hacer con el enfoque de bandera.
En la IndexProcessor
clase necesita una forma de configurar un indicador que informe al hilo que deberá terminar, similar a la variable run
que utilizó solo en el ámbito de la clase.
Cuando desee detener el hilo, configure esta bandera, llame join()
al hilo y espere a que termine.
Asegúrese de que el indicador sea seguro para subprocesos utilizando una variable volátil o métodos getter y setter que estén sincronizados con la variable que se utiliza como indicador.
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;
}
}
}
}
Luego en 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.");
}
}
}