Como você mata um Thread em Java?

374

Como você mata um java.lang.Threadem Java?

flybywire
fonte
2
até agora você não pode matar um fio; porque destruir () nunca é implementada devido ao impasse propenso
AZ_
11
Eu prefiro a resposta sobre ExecutorStatusessa questão: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby
9
@loungerdork "Eu acho que o Java deve implementar um método seguro de parada / destruição para threads descontrolados sobre os quais você não tem controle, apesar das advertências de perda de bloqueios e outras armadilhas" Então você quer uma parada de thread insegura . Eu acho que você já tem um.
DJClayworth
4
É incrível que tipo de perguntas receberia 212 votos em 2009. Isso seria imediatamente destruído hoje.
Jonathon Reinhart
7
@ JonathonReinhart: Por que isso? Parece ser uma pergunta legítima, mesmo nos dias de hoje. Talvez você não saiba a frustração que causa quando você tem threads descontrolados e só pode usar funções obsoletas para lidar com isso de alguma forma?
TFuto

Respostas:

189

Veja este tópico da Sun sobre por que eles foram reprovadosThread.stop() . Ele entra em detalhes sobre por que esse foi um método ruim e o que deve ser feito para parar com segurança os encadeamentos em geral.

A maneira que eles recomendam é usar uma variável compartilhada como um sinalizador que solicita que o encadeamento em segundo plano pare. Essa variável pode então ser definida por um objeto diferente solicitando o encerramento do encadeamento.

JaredPar
fonte
11
se você verificar o thread que interrompeu isAlive (), ele retornará true e eles continuarão adicionando ao seu ThreadGroup atual [], você pode ver isso usando Thread.currentThread.getThreadGroup (). list (); ele imprimirá todos os encadeamentos existentes e você verá várias instâncias do encadeamento se repetir o fluxo.
AZ_
3
Se você estiver em um PC, então não há problema, mas se estiver desenvolvendo um software para dispositivos móveis (o Android já o experimentei), você obterá OutOfMemoryError
AZ_
2
Pode / deve-se observar que, para garantir a comunicação imediata da solicitação de parada por meio de sinalizador, a variável deve ser volátil (ou o acesso à variável deve ser sincronizado), conforme indicado na recomendação.
Mtsz 18/06
2
Esse link foi morto neste momento. Eu consegui encontrá-lo no archive.org, no entanto: web.archive.org/web/20090202093154/http://java.sun.com/j2se/…
Jay Taylor
2
Eu uso o método getConnection()de java.sql.DriverManager. Se a tentativa de conexão demorar muito, eu tento eliminar o segmento correspondente chamando, Thread.interrupt()mas ele não influencia o segmento. As Thread.stop()obras, no entanto, embora a Oracle diga que não deve funcionar se não funcionar interrupt(). Gostaria de saber como fazê-lo funcionar e evitar o uso de método obsoleto.
Danny Lo
129

Geralmente você não ..

Você pede para interromper o que está fazendo usando Thread.interrupt () (link javadoc)

Uma boa explicação do porquê está no javadoc aqui (link do java technote)

Fredrik
fonte
@Fredrik O que acontece com o contexto do Thread quando o interrupt()método é chamado? A principal questão está relacionada à geração de logs para cada novo thread.
ABcDexter
3
@ABcDexter O ponto principal é que a interrupção não interrompe nada, apenas sinaliza para o código no thread (ou o código que está sendo chamado pelo thread) que alguém pediu para interromper o que está fazendo. Supõe-se que o encadeamento interrompa bem o processamento e o retorno, como se tivesse sido feito fazendo o que deveria (e nesse momento, o contexto do encadeamento provavelmente também será descartado). OTOH, se você realmente tivesse parado o tópico, sua pergunta seria muito boa e a resposta indefinida.
Fredrik
64

No Java, os encadeamentos não são eliminados, mas a parada de um encadeamento é feita de maneira cooperativa . É solicitado que o encadeamento seja encerrado e, em seguida, o encadeamento pode ser encerrado normalmente.

Freqüentemente um volatile boolean , é usado campo que o thread verifica e finaliza periodicamente quando é definido com o valor correspondente.

Eu não usaria a booleanpara verificar se o segmento deve terminar . Se você usar volatilecomo um modificador de campo, isso funcionará de maneira confiável, mas se o seu código se tornar mais complexo, pois, em vez disso, usa outros métodos de bloqueio dentro do whileloop, pode acontecer que o código não seja finalizado ou pelo menos demore mais enquanto você posso querer.

Certos métodos de biblioteca de bloqueio suportam interrupção.

Cada encadeamento já possui um status booleano interrompido e você deve fazer uso dele. Pode ser implementado assim:

public void run() {
   try {
      while (!interrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }
}

public void cancel() { interrupt(); }

Código fonte adaptado do Java Concurrency in Practice . Como o cancel()método é público, você pode permitir que outro thread invoque esse método como desejar.

Konrad Reiche
fonte
E o que fazer se você executar um código não confiável como um plug-in ou script? Java incorporou sandbox para código não confiável. E essa caixa de areia é inútil, pois permite trabalhar sem parar com força. Imagine que você está escrevendo um navegador em java. A capacidade de matar scripts de páginas arbitrários não tem preço.
ayvango
@ayvango Então você precisa executar esse script na sua própria caixa de areia. A sandbox Java protege a máquina do aplicativo, não partes do aplicativo.
21416 David Schwartz
@DavidSchwartz, você quer dizer que eu deveria usar outra plataforma que java?
ayvango
@ayvango Você ainda pode usar a sandbox Java para proteger a máquina do seu aplicativo. Mas se você deseja proteger partes de seu aplicativo de outras partes, precisará escolher alguma ferramenta que possa fazer isso.
David Schwartz
@DavidSchwartz ASFAIK essas ferramentas não existiriam se não forem suportadas no nível da plataforma. Mas é claro que a tarefa poderia ser resolvida com um mecanismo de script totalmente interpretado. Pode contar reduções como o erlang faz e faz outras coisas.
ayvango
20

Uma maneira é definir uma variável de classe e usá-la como sentinela.

Class Outer {
    public static volatile flag = true;

    Outer() {
        new Test().start();
    }
    class Test extends Thread {

        public void run() {
            while (Outer.flag) {
                //do stuff here
            }
        }
    }

}

Defina uma variável de classe externa, ou seja, flag = true no exemplo acima. Defina como false para 'matar' o thread.

karim79
fonte
2
Apenas como uma dica lateral: Uma variável como flag só funciona quando o thread é executado e não está preso. Thread.interrupt () deve liberar o thread da maioria das condições de espera (espera, suspensão, leitura de rede e assim por diante). Portanto, você nunca deve capturar a InterruptedException para fazer isso funcionar.
Renes
12
Isso não é confiável; faça "flag" volatilepara garantir que funcione corretamente em qualquer lugar. A classe interna não é estática, portanto, o sinalizador deve ser uma variável de instância. O sinalizador deve ser limpo em um método acessador para que outras operações (como interrupção) possam ser executadas. O nome "flag" não é descritivo.
22411 erickson
2
Eu não entendo a coisa "while" no método run. Isso não significa que o que está escrito no método run será repetido? isso não é algo que queríamos o fio para fazer, em primeiro lugar :(
2
+1 fazendo while (! Thread.currentThread (). IsInteruppted ()) é o preferido
Toby
2
Ambos os casos falham, quando, por exemplo, você abre um processo externo dentro de enquanto {// open ext process} e esse processo é interrompido, agora o thread não será interrompido nem chegará ao fim para verificar sua condição booleana e você estará deixado pendurado ... tente com, por exemplo, iniciar um console python usando java.exec e tente recuperar o controle sem escrever exit, e veja se existe uma maneira de interromper esse processo e sair .... não há como sair de tal situação ...
espaço Rocker
11

Existe uma maneira de fazer isso. Mas se você tivesse que usá-lo, você é um programador ruim ou está usando um código escrito por programadores ruins. Portanto, você deve parar de ser um programador ruim ou parar de usar esse código incorreto. Esta solução é apenas para situações em que NÃO HÁ OUTRA MANEIRA.

Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );
VadimPlatonov
fonte
2
Não há motivos para fazer isso, porque ainda é possível ligar para o público, Thread.stopmesmo que seja preterido.
Lii
11
O @Lii Thread.stopfaz o mesmo, mas também verifica o acesso e as permissões. O uso Thread.stopé bastante óbvio, e não me lembro do motivo pelo qual usei em Thread.stop0vez disso. Talvez Thread.stopnão tenha funcionado no meu caso especial (Weblogic em Java 6). Ou talvez porque Thread.stopesteja obsoleto e cause aviso.
VadimPlatonov
11
No meu cenário, essa era a única maneira de interromper um encadeamento em execução sem fim. Por alguma razão .stop () não interrompeu o thread, mas stop0 () fez
fiffy
10

Quero acrescentar várias observações, com base nos comentários acumulados.

  1. Thread.stop() interromperá um encadeamento se o gerenciador de segurança permitir.
  2. Thread.stop()é perigoso. Dito isto, se você estiver trabalhando em um ambiente JEE e não tiver controle sobre o código que está sendo chamado, pode ser necessário; consulte Por que o Thread.stop foi descontinuado?
  3. Você nunca deve parar de parar um encadeamento de trabalhador de contêiner. Se você deseja executar o código que tende a travar, (com cuidado) inicie um novo thread daemon e monitore-o, eliminando, se necessário.
  4. stop()cria um novo ThreadDeathErrorerro no segmento de chamada e lança esse erro no segmento de destino . Portanto, o rastreamento da pilha geralmente não vale nada.
  5. No JRE 6, stop()verifica com o gerenciador de segurança e depois chama as stop1()chamadas stop0(). stop0()é um código nativo.
  6. A partir do Java 13 Thread.stop()não foi removido (ainda), mas Thread.stop(Throwable)foi removido no Java 11. ( lista de discussão , JDK-8204243 )
Steve11235
fonte
8

Eu votaria em Thread.stop().

Por exemplo, você tem uma operação duradoura (como uma solicitação de rede). Supostamente, você está aguardando uma resposta, mas isso pode levar tempo e o usuário navegou para outra interface do usuário. Esse encadeamento em espera agora é a) inútil b) problema em potencial porque quando ele obtém resultado, é completamente inútil e ele aciona retornos de chamada que podem levar a vários erros.

Tudo isso e ele pode fazer um processamento de resposta que pode ser intenso na CPU. E você, como desenvolvedor, não pode nem pará-lo, porque você não pode jogarif (Thread.currentThread().isInterrupted()) linhas em todo o código.

Portanto, a incapacidade de forçar a parada de um fio é estranho.

Anfet
fonte
Se a operação de rede já for abortável com segurança, basta interromper o encadeamento. Se a operação de rede não for abortável com segurança, você não poderá ligar Thread.stop()com segurança de qualquer maneira. Você não está votando Thread.stop(), está pedindo a todas as pessoas que implementam todas as operações que podem levar muito tempo para torná-las abortáveis ​​com segurança. E isso pode muito bem ser uma boa idéia, mas não tem nada a ver com a implementação Thread.stop()como forma de solicitar o aborto seguro. Já temos interruptpara isso.
David Schwartz
"Portanto, a incapacidade de forçar uma parada forçada é estranha". - ... até que você olhe profundamente (como os designers de Java fizeram) e conclua que não há soluções tecnicamente viáveis ​​que não sejam piores do que depreciar stop.
Stephen C
5

A questão é bastante vaga. Se você quis dizer "como eu escrevo um programa para que um thread pare de funcionar quando eu quero", várias outras respostas devem ser úteis. Mas se você quis dizer “Eu tenho uma emergência com um servidor, não posso reiniciar agora e só preciso de um encadeamento específico para morrer, aconteça o que acontecer”, então você precisará de uma ferramenta de intervenção para combinar com ferramentas de monitoramento como essa jstack.

Para esse fim, criei jkillthread . Veja suas instruções de uso.

Jesse Glick
fonte
Obrigado! Exatamente o que eu estava procurando!
Denis Kokorin
4

É claro que existe o caso em que você está executando algum tipo de código não totalmente confiável. (Eu pessoalmente tenho isso ao permitir que scripts enviados sejam executados no meu ambiente Java. Sim, há um alarme de segurança tocando em todos os lugares, mas faz parte do aplicativo.) respeitar algum tipo de sinal booleano de execução / não execução. Seu único segurança decente contra falhas é chamar o método stop no encadeamento se, por exemplo, ele for executado por mais tempo do que o tempo limite.

Mas isso é apenas "decente", e não absoluto, porque o código pode pegar o erro ThreadDeath (ou qualquer exceção que você lançar explicitamente) e não repeti-lo novamente como um encadeamento cavalheiro deve fazer. Portanto, a linha inferior é AFAIA, não existe segurança absoluta contra falhas.

mlohbihler
fonte
AFAIK cada vez mais serviços estão se tornando híbridos e usam o ambiente gerenciado para executar código de terceiros (plug-in, scripts etc.) que eles não têm controle total sobre o código, não é razoável tirar completamente o thread. já que, para os engenheiros de serviço, um estado de
disponibilidade
3

Não há como matar um fio normalmente.

Você pode tentar interromper o thread, uma estratégia comum é usar uma pílula venenosa para fazer com que o thread pare

public class CancelSupport {
    public static class CommandExecutor implements Runnable {
            private BlockingQueue<String> queue;
            public static final String POISON_PILL  = stopnow”;
            public CommandExecutor(BlockingQueue<String> queue) {
                    this.queue=queue;
            }
            @Override
            public void run() {
                    boolean stop=false;
                    while(!stop) {
                            try {
                                    String command=queue.take();
                                    if(POISON_PILL.equals(command)) {
                                            stop=true;
                                    } else {
                                            // do command
                                            System.out.println(command);
                                    }
                            } catch (InterruptedException e) {
                                    stop=true;
                            }
                    }
                    System.out.println(“Stopping execution”);
            }

    }

}

BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);

http://anandsekar.github.io/cancel-support-for-threads/

Anand Rajasekar
fonte
2

Geralmente, você não mata, para ou interrompe um segmento (ou verifica se ele foi interrompido ()), mas deixa que ele termine naturalmente.

É simples Você pode usar qualquer loop junto com a variável booleana (volátil) dentro do método run () para controlar a atividade do encadeamento. Você também pode retornar do thread ativo para o thread principal para pará-lo.

Dessa forma, você normalmente mata um fio :).

Wojciech Domalewski
fonte
1

Tentativas de término abrupto de encadeamento são práticas ruins de programação bem conhecidas e evidências de um design de aplicativo ruim. Todos os threads no aplicativo multithread explicitamente e implicitamente compartilham o mesmo estado do processo e são forçados a cooperar entre si para mantê-lo consistente; caso contrário, seu aplicativo estará sujeito a erros que serão realmente difíceis de diagnosticar. Portanto, é responsabilidade do desenvolvedor fornecer uma garantia dessa consistência por meio do design cuidadoso e claro do aplicativo.

Existem duas soluções corretas principais para as terminações de threads controladas:

  • Uso da bandeira volátil compartilhada
  • Uso do par de métodos Thread.interrupt () e Thread.interrupted ().

Uma explicação boa e detalhada dos problemas relacionados ao encerramento abrupto de threads, bem como exemplos de soluções erradas e corretas para o encerramento controlado de threads, podem ser encontrados aqui:

https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop%28%29+to+terminate+threads

ZarathustrA
fonte
Como os programas não são mais escritos por um único desenvolvedor, os threads de morte geralmente são necessários. Portanto, não pode ser considerado uma programação ruim. Não consigo imaginar o Linux sem matar. Incapacidade de eliminar threads é um defeito do Java.
Pavel Niedoba 7/17
11
Impressionante, Sr. Certo ... E se você precisar chamar uma biblioteca de terceiros, sobre a qual você não tem controle e que tenha um tempo limite de buggy e possa travar uma vez a cada 1000-2000 vezes ao ser executada? Má prática de programação, hein? Bem, você nem sempre pode ter acesso ao código que está usando. De qualquer forma, o op está perguntando como matar um thread, não como controlar seu fluxo ao criar seu próprio código ...
Arturas M
A questão é o que acontecerá quando você matar um encadeamento que possui o mutex ou que possui algum bloco de memória alocado ou que deve gerar algum evento ou dados que outro encadeamento está aguardando? O que acontecerá com o restante da lógica do seu aplicativo? Você estará sempre em risco de que um problema pequeno e evidente seja convertido em um problema complexo que será difícil de reproduzir e investigar. Matar o encadeamento não é seguro, pois pode deixar seu aplicativo em vários estados inconsistentes diferentes. Dê uma olhada nas informações do link em cert.org.
ZarathustrA
Temos um segmento de controle que, em certas condições, pode decidir que todo o cálculo se tornou inútil. As muitas executáveis ​​diferentes que estão realizando o trabalho real precisam ser salpicadas com alguma variante de isInterrupted () em qualquer lugar conveniente - que horror! É muito melhor se o controlador fizer com que um ThreadDeath suba aparentemente do nada, desenrolando de maneira limpa todos os blocos sincronizados e finalmente. Todo mundo diz que isso é propenso a erros, mas para Threads não relacionados, não vejo o porquê.
Daniel
1

Como a interrupção não funcionou no Android, usei esse método, funciona perfeitamente:

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    Thread t = new Thread(new CheckUpdates());
    t.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            //Thread sleep 3 seconds
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }
Mindborg
fonte
2
Uma palavra-chave volátil deve ser adicionada ao shouldCheckUpdates, caso o compilador seja otimizado com o armazenamento local do encadeamento.
Clinux 19/04
1

'Matar um fio' não é a frase certa para usar. Aqui está uma maneira de implementar a graciosa conclusão / saída do encadeamento no vontade:

Executável que eu usei:

class TaskThread implements Runnable {

    boolean shouldStop;

    public TaskThread(boolean shouldStop) {
        this.shouldStop = shouldStop;
    }

    @Override
    public void run() {

        System.out.println("Thread has started");

        while (!shouldStop) {
            // do something
        }

        System.out.println("Thread has ended");

    }

    public void stop() {
        shouldStop = true;
    }

}

A classe acionadora:

public class ThreadStop {

    public static void main(String[] args) {

        System.out.println("Start");

        // Start the thread
        TaskThread task = new TaskThread(false);
        Thread t = new Thread(task);
        t.start();

        // Stop the thread
        task.stop();

        System.out.println("End");

    }

}
Adesh
fonte
a palavra-chave volátil deve ser adicionada à variável shouldStop, caso o compilador seja otimizado com o armazenamento local de threads.
Oleksii Kyslytsyn
0

Thread.stop está obsoleto. Como podemos parar um thread em java?

Sempre use o método de interrupção e o futuro para solicitar o cancelamento

  1. Quando a tarefa responde ao sinal de interrupção, por exemplo, a fila de bloqueio usa o método
Callable < String > callable = new Callable < String > () {
    @Override
    public String call() throws Exception {
        String result = "";
        try {
            //assume below take method is blocked as no work is produced.
            result = queue.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return result;
    }
};
Future future = executor.submit(callable);
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    logger.error("Thread timedout!");
    return "";
} finally {
    //this will call interrupt on queue which will abort the operation.
    //if it completes before time out, it has no side effects
    future.cancel(true);
}
  1. Quando a tarefa não responde ao sinal de interrupção, suponha que a tarefa execute o soquete de E / S, que não responde ao sinal de interrupção e, portanto, o uso da abordagem acima não interromperá a tarefa, o futuro expirará, mas o cancelamento no bloco finalmente não terá efeito. , o thread continuará ouvindo o soquete. Podemos fechar o soquete ou chamar o método close na conexão, se implementado pelo pool.
public interface CustomCallable < T > extends Callable < T > {
    void cancel();
    RunnableFuture < T > newTask();
}

public class CustomExecutorPool extends ThreadPoolExecutor {
    protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask < T > ) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
    public synchronized void cancel() {
        try {
            obj.close();
        } catch (IOException e) {
            logger.error("io exception", e);
        }
    }

    public RunnableFuture < T > newTask() {
        return new FutureTask < T > (this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }

        };
    }
}
Gautam Tadigoppula
fonte