[Ajuda] Sincronizar Duas Threads!

Olá, galerinha do GUJ!
Tudo beleza?

Estou com um problema aqui…
Bom, eu tenho uma classe ClienteSplash que será uma telinha Splash que criei, bem básica:

public class ClienteSplash extends JDialog
{
    public ClienteSplash(String anCliente)
    {
        setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        setModal(true);
        iniciarComponentes(anCliente);
    }

    private void iniciarComponentes(String anCliente)
    {
        JPanel anPanel = new JPanel();

        anPanel.setPreferredSize(new Dimension(300, 100));
        anPanel.setBorder(null);

        JLabel anLabel = new JLabel(String.format("Aguarde, criando a base de dados para %s...", anCliente));
        anLabel.setFont(new Font("Tahoma", 0, 11));
        
        anPanel.add(anLabel);
        setContentPane(anPanel);
        pack();
        setLocationRelativeTo(null);
    }

Daí beleza… Eu quero que essa tela seja chamada durante a execução de uma determinada operação na minha base de dados, que consume quase 8 segundos (criar um novo banco de dados).
Estou fazendo algo assim:

// Esta é a operação que deve ser executada no banco de dados.
Runnable firstRunnable = new Runnable()
{
   public void run()
   {
      try
      {
         anModelo.criarBancoCliente(anCliente);
      }
      catch (Exception err)
      {
         err.printStackTrace();
      }
   }
};

// Aqui representa a tela Splash.
Runnable secondRunnable = new Runnable()
{
   final ClienteSplash anSplash = new ClienteSplash(cf.getDados().get("nome").toString());
   public void run()
   {
      anSplash.setVisible(true);
   }
   @Override public void finalize() throws Throwable
   {
      anSplash.dispose();
   }
};

                
Thread firstThread = new Thread(firstRunnable); // Criar a base de dados.
Thread secondThread = new Thread(secondRunnable); // Mostrar a tela splash.

firstThread.start();  // Inicia operação de criar a base de dados...
secondThread.start(); // Inicia a tela splash...
Thread.sleep(8000); // Espera 8 segundos...
secondThread.interrupt(); // Some com a tela splash...
firstThread.interrupt(); // E termina a operação.

Beleza, e vem o problema: As duas threads não estão sincronizadas. Eu mando executar a primeira e, em seguida, mostrar a tela splash. Aqui tem um problema: ao invés de mostrar os componentes, mostra um quadro vazio!

Daí tá… Mostra a tela splash e tudo o mais, espera os 8 segundos… Mas a tela Splash não tá fechando depois dos 8 segundos. O registro é adicionado na tabela, mas a tela não fecha de jeito nenhum.

Alguém tem ideia de como solucionar isso?

Valeu, galera, grande abraço!

Você não precisa de threads separadas para isso. Na mesma thread que você cria o banco de dados você deve chamar o splash antes de iniciar a criação do banco, e assim que o banco for criado você esconde a splash screen. Não precisa de threads separadas não.

Ou seja, fica assim:

[code] Runnable onlyRunnable = new Runnable()
{
final ClienteSplash anSplash = new ClienteSplash(cf.getDados().get(“nome”).toString());

public void run()  
{  
   try  
   {  
      EventQueue.invokeLater(new Runnable() {
          public void run() {
                anSplash.setVisible(true);
          }
      });    

      anModelo.criarBancoCliente(anCliente);  

      EventQueue.invokeLater(new Runnable() {
          public void run() {
                anSplash.setVisible(false);
          }
      });    
   }  
   catch (Exception err)  
  {  
      err.printStackTrace();  
  }  
}  

}; [/code]

Lembre-se que para manipular componentes do Swing de outras threads, é necessário empilhar um pedido ao Swing. Por isso a necessidade da chamada do EventQueue.invokeLater.

Hmm… Ótimo, vou testar mais tarde e aviso vocês, qualquer coisa!
Então quer dizer que, para fazer um empilhamento de processos, eu devo chamar um EventQueuer para cada um?
Por exemplo…

EventQueue.invokeLater(new Runnable() {
   public void run() 
   {
      Classe.executarPrimeiroMetodo();
   }
});

EventQueue.invokeLater(new Runnable() {
   public void run() 
   {
      Classe.executarSegundoMetodo();
   }
});

EventQueue.invokeLater(new Runnable() {
   public void run() 
   {
      Classe.executarTerceiroMetodo();
   }
});

Neste caso, ele chama cada um dos processos nessa ordem que invoquei?

Se você vai rodar os três na sequência, pode colocar todos os 3 dentro do Runnable.

Note que isso aí roda dentro da thread do Swing. O seu processamento pesado não deve ser jogado pra EventQueue. Só ações que solicitam pintura e atualização de status.

[quote=ViniGodoy]Se você vai rodar os três na sequência, pode colocar todos os 3 dentro do Runnable.

Note que isso aí roda dentro da thread do Swing. O seu processamento pesado não deve ser jogado pra EventQueue. Só ações que solicitam pintura e atualização de status. [/quote]

Então digamos que o processamento de criar um novo Banco de Dados e suas respectivas tabelas é um processamento pesado. Há algum meio melhor de fazer o processamento: Mostra Splash -> Cria Banco de Dados -> Esconde Splash? Ou do jeito que você postou, através de um único Runnable, fica legal?

Fica sim. O que esse runabble faz é:

  1. Empilhar um pedido pra o Swing exibir o splash;
  2. Iniciar o processamento pesado. --> Em paralelo, a thread do Swing processa a sua fila de mensagens e chega na do Runnable, exibindo então o splash;
  3. Pedir para o swing esconder a janela. -> Em paralelo, a thread do Swing fecha a janela.