Escolar Documentos
Profissional Documentos
Cultura Documentos
Threads em Java PDF
Threads em Java PDF
Conceitos:
Diferena entre processo e thread:
Um processo um programa em execuo que possui o seu prprio espao de endereamento.
Um sistema operacional multitarefa capaz de controlar a execuo de mais de um processo
ao mesmo tempo, de forma concorrente, fazendo a CPU alternar a execuo das instrues de
cada processo.
Uma thread um fluxo sequencial de controle, ou linha de execuo, dentro de um processo
ou programa. Um processo pode assim ter diversas threads executando concorrentemente,
mas todas partilhando o mesmo espao de endereamento. Como no h necessidade de
trocar de contexto, as threads representam uma forma mais leve de processamento
concorrente.
A Mquina Virtual Java (JVM) permite que uma aplicao tenha diversas linhas de execuo
rodando concorrentemente. H sempre pelo menos uma thread, que roda o mtodo main().
Essa thread criada automaticamente, e outras podem ser criadas pelo programador. O
programa gc (garbage collector) roda em outra thread tambm iniciada pela JVM.
Porque usar threads em aplicaes:
Threads tm vrias utilidades, especialmente quando o uso de um recurso do programa pode
demandar muito tempo, e travar outras tarefas que esto aguardando, ou quando se precisa
ficar verificando a ocorrncia de algum evento para efetuar alguma ao. Nesse ltimo caso,
uma thread pode ficar dedicada apenas para verificar periodicamente a ocorrncia do evento.
A aplicao mais usual a interao com as interfaces grficas (menus, botes) quando se
quer obter resposta quase que imediata a uma ao.
A classe java.lang.Thread:
Em Java, cada thread implementada como um objeto, que deve ser uma instncia de uma
subclasse de java.lang.Thread criada para esse fim.
A interface java.lang.Runnable
Essa interface obriga a ter o mtodo public void run(). Toda thread criada para executar
os comandos de algum mtodo com essa assinatura. A prpria classe Thread possui esse
mtodo, mas vazio, ou seja, no faz nada.
Como criar uma thread:
H duas formas bsicas para se criar uma thread:
a) Criar uma subclasse explcita de java.lang.Thread, e redefinir o mtodo run()com os
comandos que a thread dever executar.
Ex:
class MinhaThread extends Thread{
public void run(){
<instrues da thread>
}
}
A partir da possvel criar quantas instncias se desejar dessa classe. Cada vez que
enviar a mensagem start() para uma instncia, uma nova linha de execuo ser iniciada
com os comandos do mtodo run(), que rodar em paralelo com as outras threads:
MinhaThread t = new MinhaThread();
t.start();
ou, simplesmente:
new MinhaThread().start();
Note que no se pode enviar a mensagem run() diretamente para um objeto Thread.
Envia-se a mensagem start(), que criar a thread onde rodaro os comandos do mtodo
run().
Obs: Threads podem ser criadas sem referncia explcita, como na forma mais simples
acima ( ex: new MinhaThread().start();) , sem risco de serem removidas pelo Garbage
Collector, pois possuem uma referncia interna que permanece at o mtodo run()
associado terminar.
Quando existem diversas threads de mesma prioridade sendo executadas, Java aloca um
tempo mximo para processar os comandos de cada uma, aps o que interrompe a thread em
execuo e passa a processar os comandos da seguinte.
O mtodo de classe Thread.currentThread():
Esse mtodo permite acessar a thread que est executando no momento em que chamado.
usado dentro do mtodo run() para permitir identificar e/ou alterar os parmetros 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());
Fazendo uma thread dormir por um certo tempo:
O mtodo de classe Thread.sleep(long milisseg) permite interromper a execuo 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 lanada 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 a prxima thread que estiver aguardando. Evita
desperdiar tempo de processamento.
Exemplos:
Os exemplos abaixo simulam uma corrida entre dois times, Flamengo e Botafogo.
Nestes dois exemplos, duas threads escrevem em paralelo na console os nmeros de 1 a 99,
seguido do nome do time.
1. No primeiro exemplo, uma subclasse de Thread definida, onde o seu mtodo run() simula
a corrida. Duas instncias dessa classe so criadas para representar os dois jogadores.
Cada thread recebe o nome de um time (Flamengo e Botafogo) passado pelo construtor.
Para tornar o resultado aleatrio, e possibilitar uma melhor observao dos resultados,
cada thread dorme uma quantidade aleatria de milisegundos entre 0 e 299 antes de
imprimir a prxima linha. Para isso usado o mtodo esttico sleep(long miliseg) da classe
Thread.
Antes de terminar, cada thread imprime a palavra TERMINOU, seguido do seu nome. O
mtodo 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() * 300));
}
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);
mengo.start();
bota.start();
System.out.println("Main terminado!");
}
}
2.
}
public class TesteDuasThreads {
public static void main (String[] args) {
Time bota = new Time("BOTAFOGO",1);
Time mengo =new Time("FLAMENGO",7);
new Thread(mengo).start();
new Thread(bota).start();
System.out.println("Main terminado!");
}
}
thread que est executando (a current thread), retornando true se o status for 1, e false em
caso contrrio.
A execuo desse mtodo tambm resseta automaticamente o status de interrupo para
zero.
Exemplos:
As classes abaixo mostram o uso de interrupo nos dois casos. Uma thread separada da
thread main() fica imprimindo indefinidamente na console os mltiplos de um valor raiz. A
impresso deve ser interrompida quando a tecla ENTER do teclado for acionada dentro do
mtodo main.
As classes devem ser usadas aos pares. O mtodo main() dispara a thread e fica aguardando
a entrada do teclado, com o comando br.readLine(). Em seguida, envia para a thread a
mensagem interrupt().
No primeiro caso, a thread usa o mtodo Thread.sleep(1) para "dormir" 1 milissegundo entre
cada impresso. O bloco catch pega a interrupo e termina a thread com return.
No segundo caso, no usado o sleep(), e por isso no existem blocos try-catch para
detectar a interrupo. O mtodo Thread.interrupted() usado para checar, a cada volta do
lao de impresso, se o status de interrupo mudou para 1.
a) Interrupo de thread por meio de InterruptedException (thread tem sleep()):
public class ImprimeThreadComSleep extends Thread{
private int raiz;
public ImprimeThreadComSleep(int raiz){
this.raiz = raiz;
}
public void run(){
int i=1;
int n=raiz;
while(true){
System.out.println(n= raiz*i);
i++;
try{
sleep(1);
}
catch(InterruptedException e){
System.out.println ("Thread interrompida enquanto dormia!");
return; // termina a thread
}
}
}
}
import java.io.*;
public class InterrompeThread1 {
public static void main(String[]args){
ImprimeThreadComSleep t;
if (args.length < 1){
System.out.println("Este programa necessita de um argumento inteiro, a raiz");
return;
}
else {
try{
int raiz = Integer.parseInt(args[0]);
t = new ImprimeThreadComSleep(raiz);
t.start();
}catch(NumberFormatException nf){
System.out.println("Este programa necessita de um argumento inteiro");
return;
}
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try{
br.readLine(); // aguarda digitar ENTER
}
catch (IOException e) {}
t.interrupt();
} // main termina
}
conseguido em Java marcando, com uma sinalizao de "tranca" ou "lock", a parte de cdigo
em que a thread faz uso de um recurso partilhado com outras threads.
O atendimento s threads que disputam um mesmo recurso no feito exatamente na forma
de uma fila de espera, pois o escalonador da JVM no muito determinstico, embora possa
seguir sugestes dadas por comandos como yield() e setPriority(). Java usa o mecanismo
de semforos.
Semforo (ou monitor, lock/tranca) e a palavra-chave synchronized.
Um semforo um sinalizador associado a um recurso que monitorado para ser usado por
uma nica thread de cada vez. O recurso sempre associado a um objeto. Todo objeto em
Java contm uma nica "tranca", que partilhada por todos os mtodos sincronizados desse
objeto.. Se o valor do semforo for zero, o objeto est disponvel.
Em Java, esse mecanismo implementado com o uso da palavra-chave synchronized para
prefixar um mtodo que deve rodar em uma thread com uso exclusivo do objeto que ativou o
mtodo. Uma thread, ao entrar em um mtodo sincronizado de um objeto, toma posse e
incrementa o semforo do objeto, impedindo outras threads de entrarem em outros mtodos
sincronizados da mesma classe, at a thread que detm a tranca terminar de executar o
mtodo.
Ao sair de um mtodo sincronizado, a thread decrementa o semforo do objeto, que ficar
liberado para uso por outras threads sincronizadas, quando o valor do semforo voltar a zero
(uma mesma thread, aps adquirir o semforo de um objeto, pode entrar em vrios mtodos
sincronizados do mesmo objeto, e cada vez que isso ocorre o semforo ser incrementado
mais uma vez).
Quando uma thread executa um mtodo sincronizado m(), que foi ativado a partir de um
determinado objeto x, como em x.m(), o acesso ao objeto x fica bloqueado a todas as demais
threads que tentarem executar esse ou outros mtodos sincronizados da classe de x, at que o
mtodo m() termine e libere a tranca.
Podemos tambm restringir o acesso a apenas um bloco de cdigo dentro de um mtodo,
prefixando o bloco com a palavra synchronized, com um objeto entre parnteses:
Ex: synchronized(x) {
<bloco de cdigo>
}
Essa construo obriga a que a tranca do objeto x deva ser adquirida por uma thread para
executar o bloco, e enquanto a tranca no for liberada por essa thread, nenhuma outra poder
executar esse bloco para esse objeto.
Trancas de Classes:
Classes tambm so objetos, e possuem trancas. A tranca de uma classe se refere aos seus
mtodos estticos sincronizados (synchronized static), e quando uma thread executa um
mtodo esttico sincronizado, o acesso s variveis de classe fica bloqueado para outras
threads.
Liberando a tranca do objeto com o wait():
De dentro de um mtodo ou bloco sincronizado, o envio da mensagem wait()ao objeto que
ativou o mtodo (this) suspende a execuo da thread, e libera a tranca desse objeto.
A chamada para wait() pode no ter argumento (forma mais usada, espera indeterminada)
ou ter um valor de tempo de espera determinado em milissegundos. A execuo do mtodo
ficar suspensa (e a thread entra no estado bloqueado) at que o tempo se esgote, ou at que
uma outra thread dispare um mtodo notify() ou notifyAll(). Nesse momento, a thread
2) Na segunda verso, foramos que o mtodo transfere() da classe Banco tenha que ser
realizado completamente (de forma atmica) por uma thread, antes de permitir que outra
thread possa assumir o controle do recurso manipulado, no caso o vetor das contas. Isso
feito prefixando o mtodo com a palavra-chave synchronized. Durante a execuo de um
Observaes:
a) Os mtodos wait(), notify(), e notifyAll() s podem ser chamados de dentro de um
bloco sincronizado. Se isso no for feito, o compilador no reclama, mas em tempo de
execuo ocorrer uma exceo do tipo IllegalMonitorStateException.
b) As chamadas de wait() devem ser feitas sempre dentro de um lao que testa a condio
para permanecer em wait, e devem estar dentro de um bloco try-catch para capturar uma
InterruptedException. Essa exceo pode ocorrer se a thread em questo receber uma
mensagem interrupt() enquanto estiver em wait, aguardando um notify().
c)
Implementao do exemplo:
Primeiro caso: sem usar sincronizao:
public class Banco {
public static final int VALOR_TOTAL = 10000; // total de cada conta
public static final int NUM_CONTAS = 10;
// nmero de contas no banco
private long conta[];
// array que armazena os valores de cada conta
private int transferencias;
// nmero de transferncias bancrias
public Banco() {
conta = new long[NUM_CONTAS];
for(int i = 0; i < NUM_CONTAS; i++) {
conta[i] = VALOR_TOTAL;
}
transferencias = 0;
teste();
}
public void transfere(int de, int para, int quantia) {
while(conta[de] < quantia) {
try {
Thread.sleep(5);
}catch(InterruptedException e) {}
}
conta[de] -= quantia;
try {
Thread.sleep(1); // para aumentar a chance da thread parar no meio
}catch(InterruptedException e) {}
conta[para] += quantia;
transferencias++;
if(transferencias % 5000 == 0)
teste();
}
public void teste() {
long soma = 0;
for(int i = 0; i < NUM_CONTAS; i++) soma = soma + conta[i];
System.out.println("No.de transaes: " + transferencias + " Soma: " +
soma);
}
} // fim da classe Banco
public class BancoSemSincronismo {
public static void main(String[] args) {
Banco b = new Banco();
for(int i=0; i < Banco.NUM_CONTAS; i++) {
new Transferencia(b, i).start();
}
}
}
public class Transferencia extends Thread {
private Banco b;
private int de;
public Transferencia(Banco b, int de) {
this.b = b;
this.de = de;
}
class Banco{
public static final int VALOR_TOTAL = 10000;
//Total de cada conta
public static final int NUM_CONTAS = 10;
//Nmero de contas no banco
private long conta[];
//Array que armazena o valor das contas
private int transferencias;
//indica o nmero de transferncias bancrias
public Banco() {
conta = new long[NUM_CONTAS];
for(int i = 0; i < NUM_CONTAS; i++) {
conta[i] = VALOR_TOTAL;
}
transferencias = 0;
teste();
}
public synchronized void transfere(int de, int para, int quantia) {
while(conta[de] < quantia) {
try {
wait();
}catch(InterruptedException e) {}
}
conta[de] -= quantia;
try {
Thread.sleep(1); // para aumentar a chance da thread parar no meio
}catch(InterruptedException e) {}
conta[para] += quantia;
transferencias++;
if(transferencias % 3000 == 0)
notify();
teste();
}
public void teste() {
long soma = 0;
for(int i = 0; i < NUM_CONTAS; i++)
soma += conta[i];
System.out.println("Transaes: " + transferencias + "
}
} // fim da classe Banco
public class BancoComSincronismo {
public static void main(String[] args) {
Banco b = new Banco();
for(int i=0; i < Banco.NUM_CONTAS; i++) {
new Transferencia(b, i).start();
}
}
}
public class Transferencia extends Thread {
private Banco b;
private int de;
public Transferencia(Banco b, int de) {
this.b = b;
this.de = de;
}
Referncias:
Bruce Eckel, Thinking in Java, 3rd Edition, disponvel em:
http://www.ibiblio.org/pub/docs/books/eckel/TIJ-3rd-edition4.0.zip
Sun Java Tutorial parte dedicada a Threads e processos concorrentes:
Concurrency: http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html