Preciso de uma solução para parar corretamente o encadeamento em Java.
Eu tenho IndexProcessor
classe 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 ServletContextListener
classe que inicia e pára o segmento:
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 classe 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)
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 o .stop();
método porque está obsoleto.
java
multithreading
listener
Paulius Matulionis
fonte
fonte
InterruptedException
. É isso que penso, mas também me pergunto como é o caminho padrão.InterruptedException
pode ser encontrada em ibm.com/developerworks/library/j-jtp05236 .Respostas:
Na
IndexProcessor
classe, você precisa definir um sinalizador que informe ao thread que ele precisará finalizar, semelhante à variávelrun
que você usou apenas no escopo da classe.Quando você deseja interromper o encadeamento, defina esse sinalizador, chame
join()
o encadeamento e aguarde o término.Verifique se o sinalizador é seguro para threads usando uma variável volátil ou métodos getter e setter que são sincronizados com a variável que está sendo usada como sinalizador.
Então em
SearchEngineContextListener
:fonte
Usar
Thread.interrupt()
é uma maneira perfeitamente aceitável de fazer isso. De fato, provavelmente é preferível a uma bandeira, como sugerido acima. O motivo é que, se você estiver em uma chamada de bloqueio interruptível (comoThread.sleep
ou usando as operações do canal java.nio), poderá realmente romper essas chamadas imediatamente.Se você usar um sinalizador, precisará aguardar o término da operação de bloqueio e poderá verificar seu sinalizador. Em alguns casos, você precisa fazer isso de qualquer maneira, como usar o padrão
InputStream
/OutputStream
que não é interrompível.Nesse caso, quando um encadeamento é interrompido, ele não interrompe a IO; no entanto, você pode fazer isso rotineiramente com facilidade em seu código (e deve fazê-lo em pontos estratégicos onde pode parar e limpar com segurança)
Como eu disse, a principal vantagem
Thread.interrupt()
é que você pode interromper imediatamente as chamadas interrompíveis, o que não é possível com a abordagem de sinalização.fonte
interrupt()
pode ser aceitável, mas em muitos outros casos não é (por exemplo, se um recurso precisar ser fechado). Se alguém alterar o funcionamento interno do loop, você precisará se lembrar de mudarinterrupt()
para o modo booleano. Eu iria para o caminho seguro desde o início e usaria a bandeira.Resposta simples: você pode interromper um tópico INTERNO de uma das duas maneiras mais comuns:
Você também pode parar os threads EXTERNAMENTE:
system.exit
(isso mata todo o processo)interrupt()
método do objeto de encadeamento *kill()
oustop()
)*: A expectativa é que isso pare um encadeamento. No entanto, o que o encadeamento realmente faz quando isso acontece depende inteiramente do que o desenvolvedor escreveu quando criou a implementação do encadeamento.
Um padrão comum que você vê nas implementações de métodos de execução é a
while(boolean){}
, onde o booleano é geralmente algo chamadoisRunning
, é uma variável membro de sua classe de encadeamentos, é volátil e geralmente acessível por outros encadeamentos por um método setter, por exemplokill() { isRunnable=false; }
. Essas sub-rotinas são boas porque permitem que o encadeamento libere todos os recursos que ele contém antes de terminar.fonte
Você sempre deve encerrar os threads verificando um sinalizador no
run()
loop (se houver).Seu tópico deve ficar assim:
Em seguida, você pode encerrar o segmento chamando
thread.stopExecuting()
. Dessa forma, o encadeamento termina limpo, mas isso leva até 15 segundos (devido ao seu sono). Você ainda pode chamar thread.interrupt () se for realmente urgente - mas a maneira preferida deve sempre estar verificando o sinalizador.Para evitar esperar 15 segundos, você pode dividir o sono da seguinte maneira:
fonte
Thread
- implementaRunnable
- você não pode chamarThread
métodos nele, a menos que o declare como umThread
caso em que não pode chamarstopExecuting()
Normalmente, um encadeamento é encerrado quando é interrompido. Então, por que não usar o booleano nativo? Tente isInterrupted ():
ref- Como posso matar um tópico? sem usar stop ();
fonte
Para sincronizar threads, prefiro usar o
CountDownLatch
que ajuda os threads a esperar até que o processo esteja sendo concluído. Nesse caso, a classe de trabalhador é configurada com umaCountDownLatch
instância com uma determinada contagem. Uma chamada para oawait
método será bloqueada até que a contagem atual atinja zero devido a invocações docountDown
método ou o tempo limite definido seja atingido. Essa abordagem permite interromper um encadeamento instantaneamente sem precisar esperar o tempo de espera especificado:Quando você deseja concluir a execução do outro encadeamento, execute countDown no encadeamento
CountDownLatch
ejoin
no encadeamento principal:fonte
Algumas informações adicionais. O sinalizador e a interrupção são sugeridos no documento Java.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
Para um encadeamento que aguarda longos períodos (por exemplo, entrada), use
Thread.interrupt
fonte
Como a interrupção não funcionou no Android, usei esse método, funciona perfeitamente:
fonte
volatile
. Consulte docs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 .Em algum momento tentarei 1000 vezes no meu onDestroy () / contextDestroyed ()
fonte