Como parar corretamente o Thread em Java?

Eu preciso de uma solução para parar corretamente o segmento em Java.

Eu tenho a class IndexProcessor que implementa a interface 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; } } } } 

E eu tenho a class ServletContextListener que inicia e interrompe o thread:

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

Mas quando eu desligo o tomcat, recebo a exceção na minha class 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) 

Eu estou usando o JDK 1.6. Então a questão é:

Como posso parar o thread e não lançar nenhuma exceção?

PS Eu não quero usar .stop(); método porque está obsoleto.

Na class IndexProcessor você precisa definir um sinalizador que informe ao encadeamento que ele precisará finalizar, semelhante à variável que você usou apenas no escopo da class.

Quando você deseja parar o thread, defina esse sinalizador e chame join() no encadeamento e espere que ele termine.

Certifique-se de que o sinalizador é thread-safe usando uma variável volátil ou usando methods getter e setter que são sincronizados com a variável sendo usada como o sinalizador.

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

Em seguida, em 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."); } } } 

O uso de Thread.interrupt() é uma maneira perfeitamente aceitável de se fazer isso. De fato, é provavelmente preferível a uma bandeira como sugerido acima. O motivo é que, se você estiver em uma chamada de bloqueio interrompível (como Thread.sleep ou usando as operações do canal java.nio), você será capaz de sair delas imediatamente.

Se você usar um sinalizador, terá que aguardar a conclusão da operação de bloqueio e verificar seu sinalizador. Em alguns casos, você precisa fazer isso de qualquer maneira, como usar o padrão InputStream / OutputStream que não são interruptivos.

Nesse caso, quando um thread é interrompido, ele não interromperá o IO, no entanto, você pode fazer isso rotineiramente no seu código (e você deve fazer isso em pontos estratégicos onde você pode parar e limpar com segurança)

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

Como eu disse, a principal vantagem do Thread.interrupt() é que você pode interromper imediatamente as chamadas interrompíveis, o que não é possível com a abordagem de sinalização.

Resposta simples: Você pode interromper um segmento INTERNALMENTE de duas maneiras:

  • O método run atinge uma sub-rotina de retorno.
  • O método de execução é concluído e retorna implicitamente.

Você também pode interromper os threads EXTERNAMENTE:

  • Chamar system.exit (isso mata todo o seu processo)
  • Chame o método interrupt() do object thread
  • Veja se o thread tem um método implementado que parece que funcionaria (como kill() ou stop() )

*: A expectativa é que isso deveria interromper um thread. No entanto, o que o encadeamento realmente faz quando isso acontece é totalmente o que o desenvolvedor escreveu quando criou a implementação do encadeamento.

Um padrão comum que você vê com implementações de método de execução é um while(boolean){} , onde o booleano é tipicamente algo chamado isRunning , é uma variável de membro de sua class de thread, é volátil e geralmente acessível por outros threads por um método setter de classifica, por exemplo, kill() { isRunnable=false; } kill() { isRunnable=false; } . Essas sub-rotinas são boas porque permitem que o encadeamento libere quaisquer resources antes de terminar.

Você deve sempre finalizar os encadeamentos, verificando um sinalizador no loop run() (se houver).

Seu segmento deve ser assim:

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

Em seguida, você pode finalizar o segmento chamando thread.stopExecuting() . Dessa forma, a discussão termina limpa, mas isso leva até 15 segundos (devido ao seu sono). Você ainda pode chamar thread.interrupt () se é realmente urgente – mas a maneira preferida deve sempre ser verificar o sinalizador.

Para evitar esperar por 15 segundos, você pode dividir o sono assim:

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

Para sincronizar encadeamentos, prefiro usar o CountDownLatch que ajuda os encadeamentos a aguardarem até que o processo seja executado. Nesse caso, a class de trabalho é configurada com uma ocorrência de CountDownLatch com uma determinada contagem. Uma chamada ao método await será bloqueada até que a contagem atual atinja zero devido a invocações do método countDown ou que o conjunto de tempo limite seja atingido. Essa abordagem permite interromper um thread instantaneamente sem ter que esperar pelo tempo 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; } } } 

Quando você quiser concluir a execução do outro thread, execute countDown no CountDownLatch e join o thread ao thread 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."); } } } 

Se estivermos usando o JDK 1.0, podemos chamar o método depreciado do thread stop () para finalizá-lo. Usar o stop () é incrivelmente perigoso, já que ele mata o seu tópico mesmo que esteja no meio de algo importante. Não há como se proteger, por isso, se você identificar o código que usa stop (), você deve fazer uma careta.

Como podemos desligar um thread de forma limpa?

Em Java, iniciar um encadeamento é fácil, mas a desativação requer muita atenção e esforços.

Aqui está como é projetado em Java. Existe um sinalizador chamado Interrupt status flag (Sinalizador de status de interrupção) em todos os encadeamentos java que podemos definir a partir do exterior, isto é, pai ou encadeamento principal. E o thread pode verificá-lo ocasionalmente e interrompe sua execução. Voluntariamente..!! Aqui está como:

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

source: Como parar o encadeamento em java | MultiThreading

Alguma informação suplementar. Sinalizador e interrupção são sugeridos no documento Java.

http://www.google.com/adwords

 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 um encadeamento que aguarda por longos períodos (por exemplo, para input), use Thread.interrupt

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

Não consegui a interrupção para funcionar no Android, então usei esse método, funciona perfeitamente:

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