A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Tutoriais e Programação

O que são threads e como domá-los

Ter

7

Abr

2009


20:39

(8 votos, média 4.75 de 5) 


Nível avançado

Se você está preocupado em domar os threads, analise cuidadosamente estas linhas de execução (a tradução de thread é linha) antes de querer tomar as rédeas smile

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 falando, 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 permitem multithreading.

Se atribuirmos um thread para uma longa sequência de cálculos, outro para uma entrada de texto e um terceiro para uma verificação ortográfica, o programa multithreaded pode fazer os cálculos requisitados enquanto o usuário digita o texto. Além disto, 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 disto, 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 para 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(), está apenas 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 isto, 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 a 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. Desta 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" para manter um applet bem comportado biggrin

Логофет Вадимкастрюли с антипригарным покрытиемлобановский класс харьковрецептыполигон ооовсе новостименю никас

Informações adicionais