Necesito una solución para detener correctamente el hilo en Java.
Tengo una IndexProcessor
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 una ServletContextListener
clase que comienza 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 apago tomcat, obtengo 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 lanzar ninguna excepción?
PD : No quiero usar el .stop();
método porque está en desuso.
java
multithreading
listener
Paulius Matulionis
fuente
fuente
InterruptedException
. Esto es lo que pienso, pero también me pregunto cómo es la forma estándar.InterruptedException
se puede encontrar en ibm.com/developerworks/library/j-jtp05236 .Respuestas:
En la
IndexProcessor
clase, necesita una forma de establecer un indicador que informe al subproceso que tendrá que terminar, similar a la variablerun
que ha utilizado solo en el ámbito de la clase.Cuando desee detener el hilo, establezca esta bandera y 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 utilizando métodos getter y setter que estén sincronizados con la variable que se utiliza como indicador.
Luego en
SearchEngineContextListener
:fuente
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á en una llamada de bloqueo interrumpible (comoThread.sleep
o usando las operaciones del canal java.nio), en realidad podrá salir de ellas de inmediato.Si usa una bandera, debe esperar a que finalice la operación de bloqueo y luego puede verificar su bandera. En algunos casos, debe hacer esto de todos modos, como el uso de estándar
InputStream
/OutputStream
que no son interrumpibles.En ese caso, cuando se interrumpe un subproceso, no interrumpirá el IO, sin embargo, puede hacerlo fácilmente de forma rutinaria en su código (y debe hacerlo en puntos estratégicos donde puede detenerse y limpiar de forma segura)
Como dije, la principal ventaja
Thread.interrupt()
es que puede salir inmediatamente de las llamadas interrumpibles, lo que no puede hacer con el enfoque de bandera.fuente
interrupt()
pueden estar bien, pero en muchos otros casos no lo está (por ejemplo, si un recurso necesita ser cerrado). Si alguien cambia el funcionamiento interno del bucle, deberá recordar cambiarinterrupt()
a la forma booleana. Yo iría por el camino seguro desde el principio y usaría la bandera.Respuesta simple: puede detener un hilo INTERNO de una de dos maneras comunes:
También puede detener hilos EXTERNAMENTE:
system.exit
(esto mata todo el proceso)interrupt()
método del objeto de hilo *kill()
ostop()
)*: La expectativa es que se supone que esto detendrá un hilo. Sin embargo, lo que el hilo hace realmente cuando esto sucede depende completamente de lo que el desarrollador escribió cuando crearon la implementación del hilo.
Un patrón común que se ve con las implementaciones de métodos de ejecución es un
while(boolean){}
, donde el booleano suele tener un nombreisRunning
, es una variable miembro de su clase de subproceso, es volátil y, por lo general, otros subprocesos pueden acceder mediante un método de establecimientokill() { isRunnable=false; }
. Estas subrutinas son buenas porque permiten que el hilo libere cualquier recurso que tenga antes de terminar.fuente
Siempre debe finalizar los hilos marcando una bandera en el
run()
bucle (si lo hay).Su hilo debería verse así:
Luego puede finalizar el hilo llamando
thread.stopExecuting()
. De esa manera, el hilo termina limpio, pero esto toma hasta 15 segundos (debido a su sueño). Todavía puede llamar a thread.interrupt () si es realmente urgente, pero la forma preferida siempre debe ser verificar la bandera.Para evitar esperar 15 segundos, puede dividir el sueño de esta manera:
fuente
Thread
- implementaRunnable
- no puedes llamarThread
métodos a menos que lo declares comoThread
en cuyo caso no puedes llamarstopExecuting()
Por lo general, un subproceso se termina cuando se interrumpe. Entonces, ¿por qué no usar el booleano nativo? Prueba isInterrupted ():
ref- ¿Cómo puedo matar un hilo? sin usar stop ();
fuente
Para sincronizar hilos prefiero usar
CountDownLatch
que ayuda a los hilos a esperar hasta que se complete el proceso. En este caso, la clase de trabajo se configura con unaCountDownLatch
instancia con un recuento determinado. Una llamada alawait
método se bloqueará hasta que el recuento actual llegue a cero debido a invocaciones delcountDown
método o se alcance el tiempo de espera establecido. Este enfoque permite interrumpir un hilo instantáneamente sin tener que esperar a que transcurra el tiempo de espera especificado:Cuando desee finalizar la ejecución del otro hilo, ejecute countDown en el hilo
CountDownLatch
yjoin
al hilo principal:fuente
Alguna información complementaria. Tanto el indicador como la interrupción se sugieren en el documento de Java.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
Para un hilo que espera por largos períodos (por ejemplo, para entrada), use
Thread.interrupt
fuente
No conseguí que la interrupción funcionara en Android, así que utilicé este método, funciona perfectamente:
fuente
volatile
. Ver docs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 .En algún momento lo intentaré 1000 veces en mi onDestroy () / contextDestroyed ()
fuente