Dúvida em run() e start()

42 respostas
T
public class Threads3 implements Runnable{
	public void run(){
		System.out.println("running");
	}

	public static void main (String args[]){
		Thread t = new Thread( new Threads3() );

		t.start();
		t.run();
		t.run();
	}
}

Por que existem vezes em que o código acima imprime apenas uma vez "running"? Não era para imprimir sempre 3 vezes?

Obrigado!

42 Respostas

D

Note que o run sendo chamado é do objeto “t”, que não possue uma implementação de “run”. Mas quando “t” chama o método “start”, o método “run” do objeto Threads3 é chamado, e aí escreve “running” na tela.

T

Como assim Douglas? O Objeto ‘t’ possui sim a implementação de run()!!! O que eu estou questionando é que, às vezes, o código imprime 3 vezes “running” e, outras vezes, imprime apenas uma vez. Para mim, deveria imprimir SEMPRE 3 vezes!!! Essa é minha pergunta!

D

Hum… foi falha minha. Eu pensei errado pelo seguinte. No código que você passou, quem está implementando o método “run” é a classe Threads3. Este método é chamado quando você chama “start” em “t”. Quando você chama “t.run()”, t está chamando o método “run” de Threads3.

Bom, quanto a sua dúvida, eu fiz um teste aqui e ta escrevendo Running sempre 3 vezes. Desculpe ae se não fui de muita ajuda.

T

De boa Douglas! Então está saindo sempre 3 vezes no seu? :shock: Estranho… Acabei de refazer o teste aqui. No meu, as vezes sai apenas uma vez também. Vamos esperar para ver se alguém sabe o que está acontecendo. :wink:

L

bem vamos lá com o run eh um metodo como qualquer outro, que vc está chamando e o start() vc faz diz a jvm que usa thread está pronta a executar, cabe a jvm executar ou nao. lembre-se com threads nao há garantias. veja se esse meu post ajuda.

abracos

W

Cara, quando você usa o método start, ele não é garantido que a nova thread que você iniciou vai ser executada na hora.
Existem claramente mais de uma possibilidade neste código.

Primeiro: O start que você usou na thread não executa uma nova thread no exato momento, executando primeiro as 2 linhas de código seguintes que são os 2 run’s, printando 2 vezes na tela a sua mensagem. E em seguida, aquele método start que você chamou antes executa depois, resultando em 3 mensagens.

Segundo: O start pode executar a thread antes dos métodos run que você chamou(algo só aparentemente mais “normal”), printando na tela apenas 1 vez a sua mensagem. As outras 2 chamadas do método run são inúteis, pois uma vez que o método start é executado e completado a sua execução a thread muda para o estado TERMINATED, onde essas chamadas de run não são válidas, não se pode usar uma thread no estado TERMINATED.

Faça testes com o método da classe Thread getState() e veja os 6 estados possíveis da Thread: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING e TERMINATED.

R

Não faz o menor sentido.

Pelas regras, deve sempre ser escrito running três vezes. Não existe isso de que se uma thread morrer os comandos após a chamada dessa thread não serem executados. Tudo sempre é executado, a não ser que haja um comando como esse na main

Thread.currentThread().setDaemon(true);

ou um wait ou algo do tipo.
Eu acho (e agora estou especulando) que foi algum problema do SO em si, alguma inconsistência.

V

Ele está certo. O método start da classe thread faz o seguinte:

a) Se a thread está associada a um Runnable, dispara a thread no método run() desse runnable;
b) Caso contrário, dispara o método run() de si mesmo.

No caso do seu código, a Thread t foi criada diretamente da classe Thread e, portanto, não tem o método run() implementado. Quando você dispara pelo start, cai na condição a), e o método run() de Threads3, que está associada, é disparado.

Quando você chama o run(), você está chamando o run() de Thread, que não está implementado.

S

Mas a minha dica é a seguinte.

1. public class Threads3 implements Runnable{  
   2.     public void run(){  
   3.         System.out.println("running");  
   4.     }  
   5.   
   6.     public static void main (String args[]){  
   7.         Thread t = new Thread( new Threads3() );  
   8.   
   9.         t.start();  
  10.         t.run();  
  11.         t.run();  
  12.     }  
  13. }

este teu codigo é suspeito , !!! :slight_smile: :slight_smile: :slight_smile: :smiley: :smiley: , ninguem entendi o porque destes dois " run " ai,

T

Ele está certo. O método start da classe thread faz o seguinte:

a) Se a thread está associada a um Runnable, dispara a thread no método run() desse runnable;
b) Caso contrário, dispara o método run() de si mesmo.

No caso do seu código, a Thread t foi criada diretamente da classe Thread e, portanto, não tem o método run() implementado. Quando você dispara pelo start, cai na condição a), e o método run() de Threads3, que está associada, é disparado.

Quando você chama o run(), você está chamando o run() de Thread, que não está implementado.]]

ViniGodoy, por esse seu raciocínio, não era para imprimir sempre apenas uma vez “running” então? Você afirmou que “Quando você chama o run(), você está chamando o run() de Thread, que não está implementado” o que eu discordo. Eu sobreescrevi ele na minha classe Threads3. Porque ao fazer t.run(); não iria ser chamado esse run() que eu sobreescrevi? Cade o polimorfismo ai?

T

renamed:
Não faz o menor sentido.

Pelas regras, deve sempre ser escrito running três vezes. Não existe isso de que se uma thread morrer os comandos após a chamada dessa thread não serem executados. Tudo sempre é executado, a não ser que haja um comando como esse na main

Thread.currentThread().setDaemon(true);

ou um wait ou algo do tipo.
Eu acho (e agora estou especulando) que foi algum problema do SO em si, alguma inconsistência.

renamed, eu também acho que não tem essa de que, se a thread morrer, não se pode mais chamar o run(); O run é um método public qualquer. Ele pode ser chamado SEMPRE. O que não se pode chamar é o start() mais de uma vez, caso contrário, uma exceção é lançada. Mesmo após a thread terminar o método run(), não se pode executar o start novamente.

Agora, a minha dúvida persiste :frowning: Vc fez testes ae também renamed? O que está imprimindo?

Abraço!

V

Ah não, você tem razão. O que tem o comportamento que descrevi é mesmo o run. Veja a implementação do método run() da classe Thread:

public void run() {
        if (target != null) {
            target.run();
        }
    }

Nesse caso, o código sempre imprimirá 3 vezes a palavra running.

Como você está fazendo para imprimir o dado? Está executando o java via console ou por alguma IDE?

V

Rodei aqui diversas vezes, e funcionou perfeitamente. Imprime 3 vezes a palavra Running.

V

TiagoTC:
ViniGodoy, por esse seu raciocínio, não era para imprimir sempre apenas uma vez “running” então? Você afirmou que “Quando você chama o run(), você está chamando o run() de Thread, que não está implementado” o que eu discordo. Eu sobreescrevi ele na minha classe Threads3.
Porque ao fazer t.run(); não iria ser chamado esse run() que eu sobreescrevi? Cade o polimorfismo ai?

Só um detalhe. O fato de executar o run não tem nada a ver com polimorfismo.

t é do tipo Thread, não do tipo Threads3. Ele não teria obrigação nenhuma de executar o seu run(), exceto pela forma como é implementado. Não é uma questão de polimorfismo. O polimorfismo só ocorreria se Threads3 fosse filho de thread, implementasse o método run(), e você criasse t como o tipo Threads3. Então, chamar o start() faria disparar o run() de Threads3, pois ele estaria sobrescrito na classe.

T

Estou no Linux. Faço “javac Threads3.java” para compilar e “java Threads3” para executar. Tudo na linha de comando mesmo. Aqui ou imprime 3 vezes “running” ou apenas uma vez…

T

ViniGodoy:
TiagoTC:
ViniGodoy, por esse seu raciocínio, não era para imprimir sempre apenas uma vez “running” então? Você afirmou que “Quando você chama o run(), você está chamando o run() de Thread, que não está implementado” o que eu discordo. Eu sobreescrevi ele na minha classe Threads3.
Porque ao fazer t.run(); não iria ser chamado esse run() que eu sobreescrevi? Cade o polimorfismo ai?

Só um detalhe. O fato de executar o run não tem nada a ver com polimorfismo.

t é do tipo Thread, não do tipo Threads3. Ele não teria obrigação nenhuma de executar o seu run(), exceto pela forma como é implementado. Não é uma questão de polimorfismo. O polimorfismo só ocorreria se Threads3 fosse filho de thread, implementasse o método run(), e você criasse t como o tipo Threads3. Então, chamar o start() faria disparar o run() de Threads3, pois ele estaria sobrescrito na classe.

Humm, é verdade, eu havia esquecido que estava implementando Runnable e não estendendo de Thread. Vlw pela explicação! :thumbup:

Mas, voltando à pergunta inicial. Será que o fato de não estar exibindo sempre 3 vezes “running” aqui seja por algum problema na JVM ?

T

Me veio outra dúvida agora. Imagina que, ao dar o t.start(), a thread não comece a executar o run() imediatamente naquele momento. Então, os outros 2 códigos serão executados ( t.run() e t.run() ), imprimindo 2 vezes “running” na tela. E se a thread main chegar ao final do seu bloco sem a thread t ter começado sua execução? Então, neste caso, iria acabar imprimindo apenas 2 vezes “running”, não? Pode acontecer esse caso?

V

Só pode acontecer se a thread t for uma deamon thread. Caso contrário, ela mantém o programa vivo.

V

Estranho, imprimir uma vez nem sequer faz sentido, já que você tem duas chamadas a t.run(). Mesmo que não houvesse aquele start(), pelo menos 2 vezes teria que ser impresso.

E

Vini se o t.run() chama o método run de Thread e nao da Threads3 quando ele da t.run(), não era pra exibir nada ou era?

V

Era, veja a implementação do run() de Thread, que coloquei um pouco abaixo.

E

Aquele target do método run significa o que?

V

É o runnable que você passou por parâmetro. Ele será null caso você use o construtor default.

E

ahhh sim perfeito, etendido então ehehe, ai sim ele chamaria os dois run() da classe Threads3 :slight_smile:

W

não faz o menor sentido.

Pelas regras, deve sempre ser escrito running três vezes. Não existe isso de que se uma thread morrer os comandos após a chamada dessa thread não serem executados. Tudo sempre é executado, a não ser que haja um comando como esse na main

nossa foi mal então, thread pode executar os comandos rsrsrs , nunca kra!
Que regras rsrsrs ve primeiro a API antes de falar o que não sabe.

W

Faz o seguinte, põe o sleep depois do start e um throws Exception no main e vcs nunca mais verão 3 mensagens na tela. Não há problema nenhum na JVM.

public class Threads3 implements Runnable{  
     public void run(){  
         System.out.println("running");  
     }  
   
     public static void main (String args[]) throws Exception {  
         Thread t = new Thread( new Threads3() );  
   
         t.start();  
         Thread.sleep(1000);
         t.run();  
         t.run();  
     }  
 }
R

Will_HRock:
não faz o menor sentido.

Pelas regras, deve sempre ser escrito running três vezes. Não existe isso de que se uma thread morrer os comandos após a chamada dessa thread não serem executados. Tudo sempre é executado, a não ser que haja um comando como esse na main

nossa foi mal então, thread pode executar os comandos rsrsrs , nunca kra!
Que regras rsrsrs ve primeiro a API antes de falar o que não sabe.

Rapaz… chega a ser frustrante ler isso de alguem que tem certificação.

Então façamos threads sem comandos, pra quê? Não sei, talvez a sua API e sua certificação digam.

W

Testa o código com o sleep e me fala porque aparece só uma vez então

V

Tem razão, após o fim da execução da thread, ele limpa o target.

É bom lembrar que esse programa aí possui 2 threads. A main thread, que executa o método run() duas vezes, e a thread disparada pela classe Thread.

O que acontece é que a thread disparada pela classe Thread limpa o runnable interno, assim que finaliza. Quando a main thread chama o método run, esse não fará nada, já que o target estará nulo.

Mas não tem nada a ver com o estado Terminated, até porque, a thread que está chamando run() diretamente é a main thread, que ainda não finalizou sua execução.

Isso é uma característica da implementação da classe Thread. Tanto, que por sobrescrita do método run, funciona, mesmo com o sleep:

public class Teste implements Runnable {
    public void run(){
       System.out.println("running");
    }
   public static void main (String args[]) throws Exception{
      Thread t = new Thread() {
            @Override
            public void run() {
                new Teste().run();
            }          
      };

      t.start();
      Thread.sleep(1000);
      t.run();
      t.run();
  }
}
R

Se vc reler o que eu escrevi primeiramente eu disse que não deveria haver nenhum sleep ou setDaemon na thread em que o método main está…
ok?

W

Não achei você falando de sleep

V

Há algo de maquiavélico nessa questão, e duvido que algo assim caia na certificação. Ela está fortemente atrelada a como a classe Thread foi implementada, não tanto no funcionamento das threads em si.

Por exemplo, quem chama run() duas vezes é a main Thread. Você, a principio, não deveria se preocupar com o estado terminated, pois você não está tentanto reiniciar thread nenhuma. Compartilhar código é algo natural entre threads, e por isso a maioria imaginou que aquele código fosse imprimir normalmente.

Mas, a questão é que a classe Thread limpa seu runnable interno, e isso é o detalhe maldoso. A única invariante que a classe Thread conceitualmente deveria garantir é não ser reiniciada, caso atinja o estado de TERMINATED, não que outra thread possa re-executar seu runnable interno.

Aliás, esse compotamento me supreendeu. Uma construção java válida, que não dará erro, mas resultará em comportamento inconsistente é essa aqui:

Thread t = new Thread(umRunnable); t.start(); t.join(); new Thread(t).start();

Porém, se t tiver terminado, não haverá erro, e o código também não fará nada. Note que não estou reutilizando threads, mas criando uma nova.

Ok, a maior parte desses códigos para certificações são mesmo abominações e esse é um deles. Mas, justamente ser dependente de implementação, e não de conceitos, é que acho que ele não deverá cair numa certificação no futuro.

R

Qualquer modificação no código em geral.

Até porque eu estava respondendo a questão do nosso amigo, cujo problema não tinha nem sleep, nem wait nem qualquer outra coisa do tipo.

Entendeu agora ou vai continuar se fazendo de mal entendido?

W

Parei, não quero discutir, só achei ruim quando você achou um absurdo o que eu tinha falado.

V

Resolvi ler o código fonte da classe Thread, e me deparei com um comentário que dizia que a thread limpava o seu runnable no exit por causa do bug 4006245.

private void exit() {
   if (group != null) {
      group.remove(this);
      group = null;
   }
   /* Aggressively null out all reference fields: see bug 4006245 */
   target = null;
   /* Speed the release of some of these resources */
   threadLocals = null;
   inheritableThreadLocals = null;
   inheritedAccessControlContext = null;
   blocker = null;
   uncaughtExceptionHandler = null;
}

O link para o bug está aqui:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4006245

A razão pela qual isso foi implementado é puramente performance. Se o objeto Thread permanecer vivo com a referência ao Runnable, este Runnable poderá demorar mais a ser coletado. Portanto, foi solicitado que o runnable fosse limpo ao final da execução da thread. Note que, novamente, esse comportamento está mais relacionado a um detalhe de implementação, não ao conceito de como threads funcionam.

Conceitualmente, seria correto esperar que o código se comportasse como imaginamos, imprimindo running 3 vezes. Porém, graças a essa otimização, isso não irá ocorrer quando a Thread finalizar.

D

Hum, deixa ver se eu entendi. Para mim, esse código deveria escrever o “Running” somente uma vez pois o objeto t da classe Thread não implementa o run(), mas sim somente a classe Threads3, cujo método run() é chamado através do método start(). Mas, de acordo com a API, enquanto a thread nova criada estiver “viva”, se você chamar o método run() do objeto da classe Thread, ele na verdade então chamará o run() do objeto Runnable atrelado a ele, é isso?
E no caso do código do Tiago, as vezes ocorria de escrever “Running” uma vez e outras 3 vezes porque a Thread “morria” antes de se escrever os outros “Running” restantes??

V

Isso. Veja a implementação do método run(), um pouco atrás. E veja também o detalhe ele limpar o runnable após a morte da thread.

Isso mesmo.

T

ViniGodoy, então aquele meu código inicial, lá do primeiro post, poderia imprimir apenas 2 vezes “running” também não? Veja:

  1. t.start() é iniciado, mas não executa o método run(). O controle volta pra main.
  2. A main executa o primeiro t.run(), imprimindo “running”.
  3. O controle volta pra thread t, onde o run() é executado, imprimindo “running” novamente. Agora, por causa daquela otimização da implementação da classe Thread, ele marca a referência target (que tinha meu Runnable) para null. O código, então, volta para a main.
  4. Na main, executa mais uma vez o t.run(). Agora, porém, o target é null e vai ser usada a implementação run() da classe Thread (que não faz nada, tem apenas um corpo fechado).

Ou seja, acabou imprimindo apenas 2 vezes! Pode acontecer isso? Eu fiz centenas de testes aqui e sempre imprimi 1 ou 3 vezes, mas acho que também existe essa possibilidade de imprimir 2 vezes…

Obrigado! :slight_smile:

V

Existe sim. Se não ocorreu, é pq provavelmente o seu escalonador não deixará uma thread rodando por tão pouco tempo no processador.

Mas isso não significa que outro micro (como um dual ou quad core) ou um outro SO não faça isso.

Aliás, esse é o inferno das aplicações multi-thread. Você tem sempre a chance daquele bug que aparece uma vez a cada 3 meses e apenas na máquina do seu cliente.

D

OPS… FÓRUM ERRADO. Desculpem

D

É meus caros, será que existe alguém que possa explicar o que realmente tá acontecendo? “OH E AGORA QUEM PODERÁ NOS AJUDAR” ?? Testei e saiu duas vezes , quando na minha mente esteja dizendo que é três!

Pra apimentar este fórum coloquei um contador! Como mostra o código abaixo:

public class Threads3 implements Runnable {
	private int i = 0;
	public void run(){
		System.out.println( "running i..:" + i);
		i++;
	}

	public static void main (String args[]){
		Thread t = new Thread( new Threads3() );
					
		t.start();
		t.run();
	}
}

E sabe qual resultado ?

running..: 0 running..: 0

R

deyvid:
É meus caros, será que existe alguém que possa explicar o que realmente tá acontecendo? “OH E AGORA QUEM PODERÁ NOS AJUDAR” ?? Testei e saiu duas vezes , quando na minha mente esteja dizendo que é três!

Pra apimentar este fórum coloquei um contador! Como mostra o código abaixo:

public class Threads3 implements Runnable {
	private int i = 0;
	public void run(){
		System.out.println( "running i..:" + i);
		i++;
	}

	public static void main (String args[]){
		Thread t = new Thread( new Threads3() );
					
		t.start();
		t.run();
	}
}

E sabe qual resultado ?

running..: 0 running..: 0

e não era pra sair 2 vezes? vc colocou apenas um start e um run… :smiley:

Sobre o contador, vc tem que sincronizar o bloco:

public class Threads3 implements Runnable {
	private int i = 0;

	public void run() {

		synchronized (this) {

			System.out.println("running i..: " + i);
			i++;
		}
	}

	public static void main(String args[]) {
		Thread t = new Thread(new Threads3());

		t.start();
		t.run();

	}
}
Criado 13 de janeiro de 2010
Ultima resposta 20 de jan. de 2010
Respostas 42
Participantes 9