Você está na página 1de 4

Threads em Java - Introdução revisão em 11/11/2008

Conceitos:
Diferença entre processo e thread:
Um processo é um programa em execução que possui o seu próprio espaço de endereçamento.
Um sistema operacional multitarefa é capaz de controlar a execução de mais de um processo
ao mesmo tempo, de forma concorrente, fazendo a CPU alternar a execução das instruções de
cada processo.

Uma thread é um fluxo seqüencial de controle, ou linha de execução, dentro de um processo


ou programa. Um processo pode assim ter diversas threads executando concorrentemente,
mas todas partilhando o mesmo espaço de endereçamento. Como não há necessidade de
trocar de contexto, as threads representam uma forma mais leve de processamento
concorrente.

Porque usar threads em aplicações:


Threads têm várias utilidades, especialmente quando o uso de um recurso do programa pode
demandar muito tempo, e travar outras tarefas que estão aguardando, ou quando se precisa
ficar verificando a ocorrência de algum evento para efetuar alguma ação. Nesse último caso,
uma thread pode ficar dedicada apenas para verificar periodicamente a ocorrência do evento.
A aplicação mais usual é a interação com as interfaces gráficas (menus, botões) quando se
quer obter resposta quase que imediata a uma ação.

A classe java.lang.Thread:

A Máquina Virtual Java permite que uma aplicação tenha diversas linhas de execução rodando
concorrentemente. Há sempre pelo menos uma thread, que roda o método main(). Essa
thread é criada automaticamente, e outras podem ser criadas pelo programador.

Em Java, cada thread é implementada como um objeto, que deve ser uma instância de uma
subclasse de java.lang.Thread criada para esse fim. Os comandos a serem executados em
cada linha de execução serão sempre os comandos de um método de nome public void
run(). A classe Thread implementa a interface Runnable, que obriga a ter o método run().

Há duas formas básicas para se criar uma thread:


a) Criar uma subclasse explícita de java.lang.Thread, e redefinir o método run() contendo
os comandos que a thread deverá executar. Na própria classe Thread já existe um método
run() que não faz nada (não é abstrato, apenas não faz nada).

Ex:
class MinhaThread extends Thread{
public void run(){
<instruções da thread>
}
}

A partir daí é possível criar quantas instâncias se desejar dessa classe. Cada vez que
enviar a mensagem start() para uma instância, uma nova linha de execução será iniciada
com os comandos do método run(), que rodará em paralelo com as outras threads:

MinhaThread t = new MinhaThread();


t.start();

ou, simplesmente:
new MinhaThread().start();

Note que não se pode enviar a mensagem run() diretamente para um objeto Thread.
Envia-se a mensagem start(), que criará a thread onde rodarão os comandos do método
run().
Obs: Threads podem ser criadas sem referência explícita, como na forma mais simples
acima ( ex: new MinhaThread().start();) , sem risco de serem removidas pelo Garbage
Collector (coletor de lixo da memória), pois possuem uma referência interna que
permanece até o método run() associado terminar.

b) A segunda forma de criar thread é recomendada em situações onde queremos executar um


método de uma classe qualquer em uma thread separada, sem que essa classe herde de
Thread. Por exemplo, se a classe já for uma subclasse de outra, não poderia herdar
também de Thread. Nesse caso, fazemos a classe em questão implementar a interface
Runnable e colocamos no método public void run() os comandos que serão executados
na thread.

No exemplo abaixo, um método run() da classe X será colocado em uma thread:

class X implements Runnable{

public void run(){


<instruções da thread>
}

..... outros métodos de X, construtores, atributos, etc


}

A seguir, em outro método, criamos uma instância de X, e passamos a instância como


argumento do construtor da classe Thread:
X x = new X(...);
Thread t = new Thread(x);

O efeito disso é a criação de uma instância de Thread que executará o método run()
definido na classe do argumento do construtor.

Enviando a seguir a mensagem start() para essa thread fará iniciar uma nova linha de
execução com os comandos do método run() da classe X, que rodará em paralelo com as
outras threads.
t.start();

Nome da thread:
Toda thread tem um nome (uma string) para poder ser identificada. O nome pode ser passado
como um parâmetro do construtor, e pode ser recuperado enviando para a thread a
mensagem getName(). Se um nome não for especificado no construtor, um novo nome será
gerado automaticamente para a thread.

Como incluir o nome na chamada do construtor:


//subclasse explicita de Thread:
Thread t1 = new MinhaThread("Thread 1");

// usando instancia de classe X que implementa Runnable:


Thread t2 = new Thread(new X(), "Thread 2");

Prioridade:
Cada thread possui uma prioridade. Threads com prioridades mais elevadas são executadas
preferencialmente antes de threads com menor prioridade. A prioridade default é 5 (em uma
escala de 1 a 10), mas pode ser alterada enviando a mensagem setPriority(int) para a
thread. (Nota: na classe Thread existem as constantes static MAX_PRIORITY, MIN_PRIORITY e
NORM_PRIORITY, com valores 10, 0 e 5, mas que podem variar dependendo da
implementação).
Quando existem diversas threads de mesma prioridade sendo executadas, Java aloca um
tempo máximo para processar os comandos de cada uma, após o que interrompe a thread em
execução e passa a processar os comandos da seguinte.
O método de classe Thread.currentThread():
Esse método permite acessar a thread que está executando no momento em que é chamado.
É usado dentro do método run() para permitir identificar e/ou alterar os parâmetros da thread
(lembre-se que o run() pode ser de outra classe). Por exemplo, para imprimir o nome da
thread onde esse comando está executando:

System.out.println(Thread.currentThread().getName());

Interrompendo a execução de uma thread por um intervalo de tempo:

O método de classe Thread.sleep(long milissegundos) permite interromper a execução da


thread pelo tempo dado no argumento.

Deve ser usado dentro de um bloco try porque, caso a thread receba uma mensagem
interrupt() enquanto estiver parada por um sleep(), será lançada uma
InterruptedException.

Cedendo o tempo de processamento para outras threads:


Uma thread pode ceder o restante do tempo de processamento alocado a ela, executando o
comando yield(). O controle passa para os comandos da próxima thread que estiver
aguardando. Evita desperdiçar tempo de processamento.

Exemplos:
1. Duas threads com igual prioridade. Cada uma imprime os 100 primeiros inteiros seguidos
da frase TERMINOU Flamengo! ou TERMINOU Botafogo! Uma thread se chama Flamengo,
e a outra, Botafogo. Após cada linha impressa, a thread "dorme" um número aleatório de
milissegundos (entre 0 e 399) para evitar que o laço de impressão termine antes de se
esgotar a fatia de tempo da thread. O nome é dado pelo argumento passado ao construtor
da thread. O método getName() da classe Thread retorna a string com o nome:

public class ThreadSimples extends Thread {


public ThreadSimples(String str) {
super(str);
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i + " " + getName());
try {
sleep((long)(Math.random() * 400));
}
catch (InterruptedException e) {}
}
System.out.println("TERMINOU " + getName()+"!");
}
}

public class TesteDuasThreads {


public static void main (String[] args) {
ThreadSimples bota = new ThreadSimples("BOTAFOGO");
bota.setPriority(4);
ThreadSimples mengo =new ThreadSimples("FLAMENGO");
mengo.setPriority(6);
Thread.currentThread().setPriority(1);
mengo.start();
bota.start();
System.out.println("Main terminado!");
}
}

2. Uso do método yield(). Força passar o controle para a próxima thread que está esperando.

// adaptado do livro 'Thinking in Java, 3rd ed.' (c) Bruce Eckel 2002
// Uso de yield() em threads.
// No metodo run() o uso de "else yield()" faz com que cada thread
// passe a vez para a proxima thread a cada volta do laco while.
public class YieldingThread extends Thread {
private int contagem = 5; //campo interno de cada thread

// Variável de classe. Contem o numero da proxima instancia a ser criada.


private static int numeroDaThread = 1;

public YieldingThread() {
super("" + numeroDaThread); // o nome da thread eh o seu numero
numeroDaThread++;
start();
}

public String toString() {


return "Thread no." + getName() + ": " + contagem;
}

public void run() {


while (true) {
System.out.println(this);
contagem--;
if (contagem == 0) return;
// sem o yield()o laço terminaria na primeira thread
else yield(); //cede a vez para a proxima thread
}
}

public static void main(String[] args) {


for (int i = 0; i < 5; i++)
new YieldingThread();
}
}

3. Exemplo de uso de sleep()


public class UmaThread extends Thread {
private int delay;
public UmaThread(String id, int delay) {
super(id);
this.delay = delay;
}
public void run() {
try {
sleep(delay);
}
catch(InterruptedException e) {
System.out.println("Thread: " + getName() + " foi interrompida");
}
System.out.println(">>" + getName() + " dormiu " + delay + " miliseg");
}
}

public class MultiThread {


public static void main(String[] args) {
UmaThread t1,t2,t3;
t1 = new UmaThread("Primeira thread",(int)(Math.random()*8000) );
t2 = new UmaThread("Segunda thread", (int)(Math.random()*8000));
t3 = new UmaThread("Terceira thread", (int)(Math.random()*8000));

t1.start();
t2.start();
t3.start();
}
}

Referências:
Sun Java Tutorial – parte dedicada a Threads e processos concorrentes:
Concurrency: http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html