É legal chamar o método start duas vezes no mesmo Thread?

89

O código a seguir leva a java.lang.IllegalThreadStateException: Thread already startedquando chamei o start()método pela segunda vez no programa.

updateUI.join();    

if (!updateUI.isAlive()) 
    updateUI.start();

Isso acontece na segunda vez que updateUI.start()é chamado. Passei por ele várias vezes e o thread é chamado e executado completamente antes de chegar updateUI.start().

Chamar updateUI.run()evita o erro, mas faz com que o thread seja executado no thread de IU (o thread de chamada, conforme mencionado em outras postagens no SO), que não é o que eu quero.

Um Tópico pode ser iniciado apenas uma vez? Em caso afirmativo, o que devo fazer se quiser executar o tópico novamente? Este thread em particular está fazendo alguns cálculos em segundo plano, se eu não fizer isso no thread, então será feito no thread de interface do usuário e o usuário terá uma espera excessivamente longa.

Vai
fonte
9
Por que você não acabou de ler o javadoc - ele descreve claramente o contrato.
mP.

Respostas:

111

Na Especificação da API Java para o Thread.startmétodo:

Nunca é legal iniciar um tópico mais de uma vez. Em particular, um thread não pode ser reiniciado depois de concluir a execução.

Além disso:

Lança:
IllegalThreadStateException- se o tópico já foi iniciado.

Portanto, sim, um Threadsó pode ser iniciado uma vez.

Em caso afirmativo, o que devo fazer se quiser executar o tópico novamente?

Se um Threadprecisar ser executado mais de uma vez, deve-se fazer uma nova instância de Threade chamá start-lo.

coobird
fonte
Obrigado. Eu verifiquei a documentação com o IDE e o tutorial Java para threads (e google também). Vou verificar a especificação da API no futuro. Esse crítico "..nunca é legal começar mais de uma vez .." não está nas outras leituras.
Será
@coobird, se eu atribuir o nome do objeto thread antigo a um novo Thread (), depois que o thread antigo terminar, o thread antigo será coletado como lixo (ou seja, ele é reciclado automaticamente ou isso deve ser feito explicitamente)?
snapfractalpop
Ele será coletado como lixo, desde que o Thread não esteja mais em execução.
voo em
1
Esta resposta está um pouco desatualizada. Se um programa Java moderno precisar executar uma tarefa mais de uma vez, não deverá criar uma nova a Threadcada vez. Em vez disso, ele deve enviar a tarefa a um pool de threads (por exemplo, java.util.concurrent.ThreadPoolExecutor)
Solomon Slow
13

Exatamente certo. Da documentação :

Nunca é legal iniciar um tópico mais de uma vez. Em particular, um thread não pode ser reiniciado depois de concluir a execução.

Em termos do que você pode fazer para cálculos repetidos, parece que você poderia usar o método invokeLater do SwingUtilities . Você já está experimentando chamar run()diretamente, o que significa que já está pensando em usar um em Runnablevez de um raw Thread. Tente usar o invokeLatermétodo apenas na Runnabletarefa e veja se isso se encaixa um pouco melhor no seu padrão mental.

Aqui está o exemplo da documentação:

 Runnable doHelloWorld = new Runnable() {
     public void run() {
         // Put your UI update computations in here.
         // BTW - remember to restrict Swing calls to the AWT Event thread.
         System.out.println("Hello World on " + Thread.currentThread());
     }
 };

 SwingUtilities.invokeLater(doHelloWorld);
 System.out.println("This might well be displayed before the other message.");

Se você substituir essa printlnchamada pelo seu cálculo, pode ser exatamente o que você precisa.

EDITAR: dando seguimento ao comentário, não tinha percebido a tag Android no post original. O equivalente a invokeLater no trabalho do Android é Handler.post(Runnable). De seu javadoc:

/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to which this handler is
 * attached.
 *
 * @param r The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */

Portanto, no mundo Android, você pode usar o mesmo exemplo acima, substituindo o Swingutilities.invokeLatercom a postagem apropriada para a Handler.

Bob Cross
fonte
O OP está perguntando sobre o encadeamento no Android, que não inclui o SwingUtilities.
Austyn Mahoney,
@Austyn, você está certo. Eu adicionei os comentários sobre Handler.post () para ilustrar o código Android paralelo.
Bob Cross,
1
Outra maneira, se você estiver apenas tentando atualizar sua IU, é usar RunOnUIThread(Runnable)ou em View.post(Runnable)vez de criar seu próprio Handler. Eles irão executar o executável no thread principal, permitindo que você atualize a IU.
Austyn Mahoney,
3

A resposta recém-chegada explica por que você não deve fazer o que está fazendo. Aqui estão algumas opções para resolver seu problema real.

Este thread em particular está fazendo alguns cálculos em segundo plano, se eu não fizer isso no thread, então será feito no thread de interface do usuário e o usuário terá uma espera excessivamente longa.

Despeje seu próprio tópico e use AsyncTask.

Ou crie um novo tópico quando precisar.

Ou configure seu thread para operar fora de uma fila de trabalho (por exemplo, LinkedBlockingQueue) em vez de reiniciar o thread.

CommonsWare
fonte
3

Não , não podemos iniciar o Thread novamente, isso lançará runtimeException java.lang.IllegalThreadStateException. >

O motivo é que uma vez que o método run () é executado por Thread, ele entra em estado morto.

Vamos dar um exemplo - pensar em iniciar a thread novamente e chamar o método start () nela (que internamente chamará o método run ()) para nós é algo como pedir a um homem morto para acordar e correr. Como, após completar sua vida, a pessoa vai para o estado morto.

public class MyClass implements Runnable{

    @Override
    public void run() {
           System.out.println("in run() method, method completed.");
    }

    public static void main(String[] args) {
                  MyClass obj=new MyClass();            
        Thread thread1=new Thread(obj,"Thread-1");
        thread1.start();
        thread1.start(); //will throw java.lang.IllegalThreadStateException at runtime
    }

}

/ * OUTPUT no método run (), método concluído. Exceção no thread "main" java.lang.IllegalThreadStateException em java.lang.Thread.start (fonte desconhecida) * /

verifique isso

Sameer Kazi
fonte
2

O que você deve fazer é criar um Runnable e envolvê-lo com um novo Thread cada vez que quiser executar o Runnable. Seria muito feio de se fazer, mas você pode encapsular um encadeamento com outro encadeamento para executar o código para ele novamente, mas só fazer isso é realmente necessário.

Peter Lawrey
fonte
qualquer snipet, como embrulhar?
Vinay
1

Como você disse, um tópico não pode ser iniciado mais de uma vez.

Direto da boca do cavalo: Java API Spec

Nunca é legal iniciar um tópico mais de uma vez. Em particular, um thread não pode ser reiniciado depois de concluir a execução.

Se você precisar executar novamente o que quer que esteja acontecendo em seu thread, você terá que criar um novo thread e executá-lo.

alanlcode
fonte
0

Reutilizar um thread é uma ação ilegal na API Java. No entanto, você pode envolvê-lo em um implemento executável e executar novamente essa instância.

Aaron He
fonte
0

Sim, não podemos iniciar o thread já em execução. Ele lançará IllegalThreadStateException em tempo de execução - se o segmento já foi iniciado.

E se você realmente precisar iniciar o thread: Opção 1) Se um Thread precisar ser executado mais de uma vez, deve-se fazer uma nova instância do Thread e chamar start nele.

riteeka
fonte
0

Um Tópico pode ser iniciado apenas uma vez?

Sim. Você pode iniciá-lo exatamente uma vez.

Em caso afirmativo, o que devo fazer se quiser executar o thread de novo? Este thread em particular está fazendo alguns cálculos em segundo plano, se eu não fizer isso no thread, então será feito no thread de interface do usuário e o usuário tiver um erro irracional longa espera.

Não execute o Threadnovamente. Em vez disso, crie Runnable e publique-o no Handler de HandlerThread . Você pode enviar vários Runnableobjetos. Se quiser enviar dados de volta ao UI Thread, com seu Runnable run()método, poste um Messageno HandlerUI Thread e processehandleMessage

Consulte esta postagem para obter um exemplo de código:

Android: brinde em uma conversa

Ravindra babu
fonte
0

Não sei se é uma boa prática, mas quando deixo run () ser chamado dentro do método run (), ele não gera nenhum erro e realmente faz exatamente o que eu queria.

Eu sei que não é um tópico de discussão novamente, mas talvez isso seja útil para você.

public void run() {

    LifeCycleComponent lifeCycleComponent = new LifeCycleComponent();

    try {
        NetworkState firstState = lifeCycleComponent.getCurrentNetworkState();
        Thread.sleep(5000);
        if (firstState != lifeCycleComponent.getCurrentNetworkState()) {
            System.out.println("{There was a NetworkState change!}");
            run();
        } else {
            run();
        }
    } catch (SocketException | InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Thread checkingNetworkStates = new Thread(new LifeCycleComponent());
    checkingNetworkStates.start();
}

Espero que isso ajude, mesmo que seja só um pouco.

Felicidades

Diorcula
fonte
-1

Seria muito feio de se fazer, mas você pode encapsular um encadeamento com outro encadeamento para executar o código para ele novamente, mas só fazer isso é realmente necessário.

Tive que consertar um vazamento de recursos causado por um programador que criou um Thread, mas em vez de iniciá-lo (), ele chamou o método run () - diretamente. Portanto, evite-o, a menos que você realmente saiba quais efeitos colaterais ele causa.

Torben
fonte
Mas a sugestão não era chamar run()diretamente, apenas incorporar um Runnable em um Thread e presumivelmente invocar start().
H2ONaCl
@ H2ONaCl Se você leu o texto que citei, a sugestão foi envolver um Thread em um Thread. Pode ser que você não tenha lido a sugestão original antes de editá-la.
Torben