thread reiniciar reanudar pausar hilos fuente entre ejemplos ejemplo diferencia detener como codigo java multithreading listener

reanudar - reiniciar un hilo en java



¿Cómo detener correctamente el hilo en Java? (8)

Necesito una solución para detener correctamente el hilo en Java.

Tengo una clase IndexProcessor 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 la clase ServletContextListener 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 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. Así que la pregunta es:

¿Cómo puedo detener el hilo y no lanzar excepciones?

PS No quiero usar .stop(); Método porque está en desuso.


Alguna información complementaria. Tanto la bandera como la interrupción se sugieren en el documento de Java.

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

Para un hilo que espera largos períodos (por ejemplo, para entrada), use Thread.interrupt

public void stop() { Thread moribund = waiter; waiter = null; moribund.interrupt(); }


En la clase IndexProcessor , necesita una forma de establecer un indicador que informe al subproceso que necesitará terminar, similar a la run la variable que ha utilizado solo en el ámbito de la clase.

Cuando desee detener el hilo, establezca este indicador y llame a join() en el hilo y espere a que termine.

Asegúrese de que la bandera sea segura para subprocesos mediante el uso de una variable volátil o mediante métodos de obtención y establecimiento que estén sincronizados con la variable que se utiliza como la bandera.

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


No conseguí que la interrupción funcionara en Android, así que usé este método, funciona perfectamente:

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


Para sincronizar hilos, prefiero usar CountDownLatch que ayuda a los hilos a esperar hasta que el proceso se complete. En este caso, la clase de trabajo se configura con una instancia de CountDownLatch con un recuento determinado. Una llamada al método de await se bloqueará hasta que el conteo actual llegue a cero debido a las invocaciones del método countDown o al tiempo de espera establecido. Este enfoque permite interrumpir un hilo instantáneamente sin tener que esperar a que transcurra el tiempo de espera especificado:

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

Cuando desee finalizar la ejecución del otro hilo, ejecute countDown en CountDownLatch y únalo al hilo principal:

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


Respuesta simple: puede detener un hilo INTERNALMENTE de una de las dos formas comunes:

  • El método de ejecución golpea una subrutina de retorno.
  • El método de ejecución finaliza y vuelve de forma implícita.

También puedes detener los hilos EXTERNO:

  • Llame a system.exit (esto mata todo su proceso)
  • Llamar al método interrupt() del objeto thread *
  • Ver si el hilo tiene un método implementado que suena como que funcionaría (como kill() o stop() )

*: La expectativa es que se supone que esto detenga un hilo. Sin embargo, lo que realmente hace el subproceso cuando esto sucede depende completamente de lo que escribió el desarrollador cuando creó la implementación del subproceso.

Un patrón común que se ve con las implementaciones de los métodos de ejecución es un while(boolean){} , donde el booleano normalmente se denomina isRunning , es una variable miembro de su clase de hilo, es volátil y, por lo general, se puede acceder a él mediante otros métodos mediante un método de establecimiento de ordena, por ejemplo, kill() { isRunnable=false; } kill() { isRunnable=false; } . Estas subrutinas son agradables porque permiten que el subproceso libere los recursos que contiene antes de terminar.


Si estamos usando JDK 1.0, podemos llamar al método obsoleto de Thread stop () para terminarlo. Usar stop () es increíblemente peligroso, ya que matará tu hilo incluso si está en medio de algo importante. No hay forma de protegerse, por lo que si ve un código que usa stop (), debería fruncir el ceño.

¿Cómo cerramos un hilo limpiamente?

En Java, iniciar un subproceso es fácil, pero cerrarlos requiere mucha atención y esfuerzo.

Así es como está diseñado en Java. Hay una marca llamada Interrupción de estado en cada subproceso de Java que podemos establecer desde el exterior, es decir, la principal o principal. Y el hilo puede comprobarlo de vez en cuando y detiene su ejecución. Voluntariamente..!! Aquí es cómo:

Thread loop = new Thread(new Runnable() { @Override public void run() { while (true) { if (Thread.interrupted()) { break; } // Continue to do nothing } }}); loop.start(); loop.interrupt();

fuente: Cómo detener hilo en java | Multihilo


Siempre debe finalizar los subprocesos marcando una bandera en el bucle run() (si existe).

Tu hilo debe verse así:

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

Luego puedes terminar el hilo llamando a thread.stopExecuting() . De esa manera, el hilo se termina de limpiar, pero esto toma hasta 15 segundos (debido a su sueño). Aún puede llamar a thread.interrupt () si es realmente urgente, pero la forma preferida siempre debería estar marcando la bandera.

Para evitar esperar 15 segundos, puedes dividir el sueño de esta manera:

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


Usar Thread.interrupt() es una forma perfectamente aceptable de hacer esto. De hecho, es probable que 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 las operaciones del canal java.nio), podrás salir de esas de inmediato.

Si usa una bandera, tiene que esperar a que finalice la operación de bloqueo y luego puede revisar su bandera. En algunos casos, tiene que hacer esto de todos modos, como el uso de InputStream / OutputStream estándar que no se pueden interrumpir.

En ese caso, cuando se interrumpe un hilo, no interrumpirá el IO, sin embargo, puede hacerlo fácilmente en su código (y debe hacerlo en puntos estratégicos donde puede detenerse y limpiar de forma segura)

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

Como he dicho, la principal ventaja de Thread.interrupt() es que puede interrumpir inmediatamente llamadas interrumpibles, lo que no puede hacer con el enfoque de la bandera.