Thread com loop

Ola pessoal,

Estou fazendo um programa pra uma empresa de hospedagem que pega a DNS de uma lista de sites e para agilizar o processo estou utilizando Threads. Só que da maneira que estou fazendo estou encontrando um problema…Vejam:

            for (int i = 0; i < arraySites.size(); i++) {

                new Thread(new ConsultaDNS(arraySites.get(i), 1).start();
                new Thread(new ConsultaDNS(arraySItes.get(i), 21)).start();
                new Thread(new ConsultaDNS(arraySites.get(i), 41)).start();
                new Thread(new ConsultaDNS(arraySites.get(i), 61)).start();
                new Thread(new ConsultaDNS(arraySites.get(i), 81)).start();
                new Thread(new ConsultaDNS(arraySites.get(i), 101)).start();

               //Aki eu gostaria de esperar todas as threads acabarem e ir pra proxima iteração

            }

Só que meu problema é o seguinte, nunca mechi com threads e pelo o que observei, ta indo tudo direto. Entao eu precisaria esperar todas as threads que startei terminarem para passar pra proxima iteração e assim por diante. Só que nao sei fazer isso, alguém tem uma sugestão?

Obrigado!

cola esse metodo consultaDNS ae pra vermos…
metodo nao, classe.

Dá pra usar o metodo join das threads, mas desconfio q de pra fazer isso q vc quer fazer de outra forma.

Na verdade esse CosultaDNS é uma classe que implementa Runnable aí dentro do método Run() eu realizo as ações…

[code]for (int i = 0; i < arraySites.size(); i++) {
Thread threads[] = {
new Thread(new ConsultaDNS(arraySites.get(i), 1)),
new Thread(new ConsultaDNS(arraySItes.get(i), 21)),
new Thread(new ConsultaDNS(arraySites.get(i), 41)),
new Thread(new ConsultaDNS(arraySites.get(i), 61)),
new Thread(new ConsultaDNS(arraySites.get(i), 81)),
new Thread(new ConsultaDNS(arraySites.get(i), 101))
};
//Põe as threads para processar
for (Thread t : threads) {
t.start();
}

//Aguarda o fim do processamento
//IMPORTANTE: não ponha esse código no for de cima.
for (Thread t : threads) {
   t.join();
}

}[/code]

Melhor ainda se você usar um ExecutorService. Ele permitirá o reaproveitamento de threads entre as execuções do seu loop, ganhando tempo ao evitar criação/destruição de threads.

Opa valeu ViniGodoy.

Vou pesquisar sobre esse ExecutorService msm pois, quando uso join as threads não adicionam as dns em um jTextArea em tempo real, o programa adiciona tudo de uma vez apenas quando o processo termina
:frowning:

Nesse caso, poste a classe ConsultaDNS. O problema pode não ter nada a ver com essas threads.

Por acaso esse for está dentro de um evento de botão direto? Se estiver, você terá que move-lo para uma thread separada também.

Eu estou usando o for dentro do botão sim… Eu teria que criar uma thread exclusiva para ação dele? Como?


public class ConsultaDNS implements Runnable {

    private String site = null;
    private int numero = 0;

    public ConsultaDNS(String site, int numero) {
        this.site= site;
        this.numero = numero;

    }

    public void run() {
        iniciarConsultaDNS();
  
    }

    public void iniciarConsultaDNS() {

        try {
                InetAddress endereco = pegarHost(this.site, this.numero);
                if (endereco != null) {
                   String dns = getHostAddress().concat("\n");
                    Principal.jTextAreaDNS.append(dns);
                }

            } catch (UnknownHostException ex) {
            }
   }

}

Teria sim:

[code]new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < arraySites.size(); i++) {
Thread threads[] = {
new Thread(new ConsultaDNS(arraySites.get(i), 1)),
new Thread(new ConsultaDNS(arraySItes.get(i), 21)),
new Thread(new ConsultaDNS(arraySites.get(i), 41)),
new Thread(new ConsultaDNS(arraySites.get(i), 61)),
new Thread(new ConsultaDNS(arraySites.get(i), 81)),
new Thread(new ConsultaDNS(arraySites.get(i), 101))
};
//Põe as threads para processar
for (Thread t : threads) {
t.start();
}

      //Aguarda o fim do processamento
      //IMPORTANTE: não ponha esse código no for de cima.
      for (Thread t : threads) {
         t.join();
      }

}).start();
}[/code]

Se as consultas de DNS não precisarem ser feitas em paralelo, talvez nem mesmo seja necessária as várias threads internas.

Você tem que saber que o Swing gerencia tudo numa única fila. É como se fosse assim:

while (true) { processaEventos(); repintaTela(); }

Se seu código "travar" processando algum evento, toda a tela será congelada, pois o while do swing nunca chegará ao comando "repinta a tela".
Por isso, para que vc faça um processamento grande, e pinte a tela ao mesmo tempo, é necessário deslocar esse processamento para uma thread separada. Assim seu evento de botão retorna imediatamente, deixando o swing livre para processar esse while.

Note que, para a atualização de tela, de nada adianta você disparar várias threads em paralelo, se no final seu botão fazia os join() e esperava esse processamento terminar. Só quando o botão terminava, é que o swing tinha oportunidade de repintar a tela.

[quote=ViniGodoy]Teria sim:

[code]new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < arraySites.size(); i++) {
Thread threads[] = {
new Thread(new ConsultaDNS(arraySites.get(i), 1)),
new Thread(new ConsultaDNS(arraySItes.get(i), 21)),
new Thread(new ConsultaDNS(arraySites.get(i), 41)),
new Thread(new ConsultaDNS(arraySites.get(i), 61)),
new Thread(new ConsultaDNS(arraySites.get(i), 81)),
new Thread(new ConsultaDNS(arraySites.get(i), 101))
};
//Põe as threads para processar
for (Thread t : threads) {
t.start();
}

      //Aguarda o fim do processamento
      //IMPORTANTE: não ponha esse código no for de cima.
      for (Thread t : threads) {
         t.join();
      }

}).start();
}[/code]

Se as consultas de DNS não precisarem ser feitas em paralelo, talvez nem mesmo seja necessária as várias threads internas.

Você tem que saber que o Swing gerencia tudo numa única fila. É como se fosse assim:

while (true) { processaEventos(); repintaTela(); }

Se seu código "travar" processando algum evento, toda a tela será congelada, pois o while do swing nunca chegará ao comando "repinta a tela".
Por isso, para que vc faça um processamento grande, e pinte a tela ao mesmo tempo, é necessário deslocar esse processamento para uma thread separada. Assim seu evento de botão retorna imediatamente, deixando o swing livre para processar esse while.

Note que, para a atualização de tela, de nada adianta você disparar várias threads em paralelo, se no final seu botão fazia os join() e esperava esse processamento terminar. Só quando o botão terminava, é que o swing tinha oportunidade de repintar a tela.[/quote]

Pow ViniGodoy brigadão mesmo cara!

Era isso mesmo que vc falou, eu precisava de uma nova thread pra executar a ação do botão! Agora funcionou sem travar nada! Fico te devendo essa! Mas inevitavelmente vou ter que estudar mais sobre threads!

Valew cara!

Ae Vinny só mais uma dúvida. No código que vc me passou, no for que faz o join, vc escreveu lá assim: IMPORTANTE: Não ponha esse código no for de cima.

new Thread(new Runnable() {  
   @Override  
   public void run() {  
      for (int i = 0; i < arraySites.size(); i++) {  
          Thread threads[] = {  
                new Thread(new ConsultaDNS(arraySites.get(i), 1)),  
                new Thread(new ConsultaDNS(arraySItes.get(i), 21)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 41)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 61)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 81)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 101))  
          };  
         //Põe as threads para processar  
         for (Thread t : threads) {  
             t.start();  
         }  
  
          //Aguarda o fim do processamento  
          //IMPORTANTE: não ponha esse código no for de cima.  
          for (Thread t : threads) {  
             t.join();  
          }  
   }).start();  
}  

Só que ai eu fiz isso, coloquei ele depois do primeiro form:

new Thread(new Runnable() {  
   @Override  
   public void run() {  
      ArrayList<Thread> arrayThreads = new ArrayList<Thread>();
      for (int i = 0; i < arraySites.size(); i++) {  
          Thread threads[] = {  
                new Thread(new ConsultaDNS(arraySites.get(i), 1)),  
                new Thread(new ConsultaDNS(arraySItes.get(i), 21)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 41)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 61)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 81)),  
                new Thread(new ConsultaDNS(arraySites.get(i), 101))  
          };  
         //Põe as threads para processar  
         for (Thread t : threads) {  
             t.start();  
            arrayThreads.add(t);
         }  
    }
  
          //Aguarda o fim do processamento  
          //IMPORTANTE: não ponha esse código no for de cima.  
          for (Thread t : arrayThreads) {  
             t.join();  
          }  
   }).start();  
}  

Só que se eu tirar ele de dentro do for eu recebo o erro:

Exception in thread "Thread-11" java.lang.OutOfMemoryError: unable to create new native thread

Pra evitar esse erro eu diminui de 6 para 4 o numero de threads… Eu so nao entendi pq o join nao pode ficar dentro do primeiro for… ele não serve para conter a execução das threads?

Sim. Mas se vc colocar dentro do primeiro for, vc vai esperar cada thread acabar, para só então iniciar a próxima. Se for para fazer uma thread de cada vez, aí nem precisa ter várias threads, use uma só.
Da forma que ressaltei, vc dispara todas as threads em paralelo. Elas trabalham juntas e no for debaixo vc espera que terminem.

PS: Você pode colocar suas threads no arraylist desde o início, sem usar aquele array primitivo que declarei ali.

Peraí, vc mudou o meu for. Ele estava dentro do for dos arraySizes. Isso fica como está.

Só falei para não colocar o join() logo após o start().

Com o arrayList ficaria assim:

[code]new Thread(new Runnable() {
@Override
public void run() {
for (String site : arraySites) {
List<Thread> threads = new ArrayList<Thread>();
threads.add(new Thread(new ConsultaDNS(site, 1)));
threads.add(new Thread(new ConsultaDNS(site, 21)));
threads.add(new Thread(new ConsultaDNS(site, 41)));
threads.add(new Thread(new ConsultaDNS(site, 61)));
threads.add(new Thread(new ConsultaDNS(site, 81)));
threads.add(new Thread(new ConsultaDNS(site, 101)));

     //Põe as threads para processar  
     for (Thread t : threads) {  
         t.start();  
     }  
     //Espera que todas parem
     for (Thread t : threads) {  
         t.join();  
     }  
  }

}).start();
} [/code]

[quote=ViniGodoy]Peraí, vc mudou o meu for. Ele estava dentro do for dos arraySizes. Isso fica como está.

Só falei para não colocar o join() logo após o start().[/quote]

Ah tá… foi mal cara eu tinha entendido errado… achei que não era pra colocar dentro do laço principal.

Mas muito obrigado pelas suas explicações, JAVA envolve muito conceito mas infelizmente estou sem tempo para estudá-los.

Obrigado.

É que muita gente vê isso:

         //Põe as threads para processar    
         for (Thread t : threads) {    
             t.start();    
         }    
         //Espera que todas parem  
         for (Thread t : threads) {    
             t.join();    
         }    [/code]

E fica tentado a transformar nisso:
[code]         //Põe as threads para processar    
         for (Thread t : threads) {    
             t.start();    
             t.join();    
         }    

Sem se ligar que desse segundo jeito, uma thread inicia, aí o java espera terminar. Aí outra inicia, aí o java espera…
O que efetivamente roda 1 thread de cada vez.

E do primeiro jeito, todas começam juntas, processam juntas, e só no final ele espera que todas terminem.