Informática NumaBoa Aprenda Java fazendo Applets
Conceitos Avançados:
O QUE SÃO THREADS E COMO DOMÁ-LOS
Java NumaBoa



O QUE É UM THREAD

A tradução literal de thread é linha e, por incrível que pareça, corresponde a uma linha de execução do programa. Tecnicamente, um thread é um único fluxo sequencial de execução. A linguagem Java permite que seu programa execute (ou pareça estar executando) mais de uma linha simultaneamente em sistemas operacionais que permitam multithreading.

Se atribuirmos um thread para uma longa sequencia de cálculos, outro para entrada de texto e um terceiro para verificação ortográfica, o programa multithreaded pode fazer os cálculos requisitados enquanto o usuário digita o texto. Além disso, enquanto o texto é digitado, a ortografia do mesmo é verificada. Isto é multi-linha (multithreading) e a impressão que se tem é de que tudo ocorre ao mesmo tempo.

Na verdade, a linguagem Java alterna rapidamente o controle de um thread para o outro, permitindo que cada um rode um breve período de tempo antes de passar o controle para o próximo.


THREADS E APPLETS

Toda classe Applet que inicializa seus applets funciona como parte de um thread. Além disso, você pode criar threads adicionais para controlar determinadas tarefas. Eles podem ser ligados, desligados, criados e extintos.

Threads são como CPUs virtuais rodando na Java. Cada thread criado consome recursos de máquina e, outro aspecto interessante, são dos poucos objetos Java que não são eliminados automaticamente através da garbage collection (coleta de lixo). Portanto, threads que não sejam mais necessários precisam ser extintos "na unha".

Imagine vários applets, cada um deles com vários threads ativos, numa mesma página HTML. Quem é que põe ordem no pedaço ? O xerife chama-se thread principal da AWT da Máquina Virtual Java (JVM). O xerife é avisado assim que um thread é criado ou extinto e, cada thread em atividade, envia seus pedidos a ele, que coordena e atende as chamadas de todos.

É muito fácil criar threads. Gerenciá-los corretamente para não perder o controle sobre os mesmos é que são elas... É necessário conhecer, dominar e usar threads com critério para não gerar applets mal comportados, malucos mesmo, que consumam todos os recursos da máquina e façam o xerife cometer suicídio.


MELHORE A PERFORMANCE ALIVIANDO O TRABALHO DO XERIFE

A função do thread "xerife" é gerenciar e atender os pedidos de vários threads de execução. Pintar a janela dos applets é uma função exclusiva do thread "xerife". Quanto mais o thread "xerife" estiver ocupado com tarefas que não sejam da sua alçada, menos tempo terá para atender as solicitações recebidas dos threads de execução (dos applets, por exemplo).

Uma técnica para melhorar a performance dos aplicativos é fazer com que os threads dos applets façam todo o trabalho de cálculos, tratamento de exceções, atribuição de variáveis, etc, e deixar para o xerife apenas as funções de gerenciamento e de manutenção das janelas (pintura). Por exemplo:


   // Este método é executado pelo thread do applet
   public void run() {
      while (true) {
         // ... Código complexo aqui ...
         // Terminado o trabalho elaborado, chamar o xerife com
         repaint();
         sleep(50);
      }
   }

   // Este método é executado pelo thread "xerife"
   public void paint(Graphics g) {
      g.drawImage(imagem, 0, 0, this);
   }


NÃO DIVIDA TRABALHO DOS THREADS COM O XERIFE

Quando você chama repaint(), você apenas está fazendo um pedido. O xerife pode ignorá-lo, esperar por mais pedidos do mesmo tipo ou primeiro anotar todos os pedidos para depois executá-los. Portanto, evite colocar processamentos pesados nos métodos update() e paint(). Evite também que variáveis sejam atualizadas nestes métodos.

Exemplo de código ruim:


   public void run() {
      quadroNro = 0;
      while (true) {
         mudaQuadro(quadroNro);
         repaint();
         sleep(50);
      }
   }

   public void paint(Graphics g) {
      g.drawImage(quadroNro, 0, 0, this);
      quadroNro++;
   }

Neste caso, a variável quadroNro é incrementada no método paint(). Isto não vai sobrecarregar o xerife porém, caso ele resolva aguardar, a variável não será incrementada. Enquanto isso, o método run() faz outra chamada a mudaQuadro com o mesmo valor de quadroNro, sobrecarregando o sistema e perdendo tempo.


ÁREAS MENORES DÃO MENOS TRABALHO

Nem sempre é necessário repintar toda a área do applet, fundo e frente. Caso o fundo não tenha que ser atualizado, sobreponha o método update() do xerife, desviando-o para o método paint() como a seguir:


   public void update(Graphics g) {
      paint(g);
   }

Se apenas uma pequena porção da janela do applet precisar de pintura, solicite ao xerife apenas a pintura da pequena área retangular correspondente:


   public void run() {
      while (true) {
         // ... Código complexo aqui ...
         areaPeq = mudaQuadro(quadroNro);
         repaint(areaPeq.x, areaPeq.y, 
            areaPeq.width, areaPeq.height);
         sleep(50);
      }
   }


DORMIR CONSOME TEMPO... E RECURSOS

Seja econômico com o método sleep(). A maioria dos applets, mesmo os que tenham animação, podem perfeitamente ficar sem dormir (sleep). Para processar um sleep() é necessário calcular quanto tempo o thread deve permanecer inativo para depois "acordá-lo", e o processador precisa calcular este tempo.

Podemos usar uma forma alternativa mais enérgica através de yield(). O método yield() informa o xerife que o thread está parado e que dá preferência a outros threads se o processador estiver ocupado. Se o processador estiver livre, o thread parado é imediatamente reativado. O tempo de espera é muito menor e o processador não precisa olhar para o relógio uma única vez.


AO INVÉS DE DORMIR... ESPERAR

Caso um thread tenha terminado seu processamento, por exemplo, uma sequência de quadros numa animação, podemos indicar uma espera da seguinte forma:


   while (true) {
      if (correndo) {
         ... continua a animação ...
} else { sleep(1000); } }

A variável lógica correndo é que determina se animação é mostrada ou não. Digamos que ela possa ser controlada pelo clique do mouse. Se correndo for falso, sleep(1000) faz com que o thread cheque a cada 1 segundo se a animação está correndo ou não. Dessa forma o thread não está incomodando o processador a maior parte do tempo, mas o reinício da animação sofre um atraso perceptível. O código acima é bom, porém há um modo de obter um resultado melhor:


   while (true) {
      if (correndo) {
         ... continua a animação ...
      } else {
         wait();
      }
   }

O thread fica em espera, congelado... para reativá-lo, basta que qualquer outro thread chame o método "notify".


EVITE A EUTANÁSIA

Evite "matar" seus threads. Imagine um método run() no seu applet que contenha um while (true) e o seguinte método stop():


   public void stop() {
      if (meuThread != null)
         meuThread.stop();
   }

Quando o navegador muda de página, meuThread é desativado. O método run(), porém, continua e meuThread não desaparece. Threads só desaparecem quando têm morte natural. Podemos fazer isso através de uma variável lógica:


   private boolean ativo = true;

   public void run() {
      while (ativo) {
         ... faz o que é preciso ...
      }
   }

   public void stop() {
      ativo = false;
   }

Escrevendo o código desta forma faz com que a animação morra naturalmente quando chegar a sua hora, e não prematuramente. Nada como uma morte "saudável" ;-))))))




| AAAA | Página Inicial | Mapa do Site | Novidades | Busca | Indique esta página | Mestre da Teia | Voltar |
| Localizador || @ Info NumaBoa > Java NumaBoa > Como domar os threads
Créditos: vovó Vicki. Em especial para David Griffiths, o mago dos applets.

webdesign sobMedida by vickiSoft - /informatica/java/thread.php (05.01.01) versão 1.1 de 14.07.03
Licença Creative Commons 1998-2005 Aldeia NumaBoa
Exceto onde especificamente declarado, todo material deste site é disponibilizado de acordo com a Licença Creative Commons.