Você está na página 1de 55

Threads

Marco Antonio
Arquiteto de Software
Fevereiro/2009
Definição
• Praticamente todos os SO's suportam o conceito de processo (programas
independentes que estão isolados dos demais).
• O mecanismo de thread surge com o objetivo de permitir múltiplas atividades
dentro de cada processo.
• Java foi a primeira linguagem (dentre as famosas) que explicitamente incluiu
thread como recurso interno, ao invés de delegar essa tarefa para o sistema
operacional.
• Assim como os processos, as threads são independentes. São caminhos
independentes de execução dentro de um mesmo programa.
• Um processo Java suporta múltiplas threads, que parecem estar sendo
executadas simultaneamente e de forma independente.
• A API de threads em Java é bastante simples. Infelizmente, escrever
programas complexos usando múltiplas threads não é tão simples.
• As threads compartilham a mesma memória, as mesmas variáveis. Dessa
forma, você tem que tomar cuidado para garantir que uma thread não interfira
na execução das demais.
Motivação
• Eficiência
– Download de arquivos pela rede
• Conveniência
– Um relógio
• Aplicações multi-client
– JEE, Swing, RMI, Servidor web, servidor de e-mail
• Transparência
– Existem threads automáticas: garbage collection,
finalização de objetos e outras tarefas de
background da JVM.
Processamento assíncrono
• Algumas aplicações trocam informações com
recursos remotos (através da rede, por exemplo).
• Quando você lê bytes de um socket, caso não haja
nenhum dado disponível, a aplicação ficará
bloqueada até que essa situação se altere, liberando
acesso a outros recursos.
• Dessa forma, enquanto a aplicação não tiver bytes
para ler, poderá continuar outras linhas de execução.
• Não precisamos fazer nenhuma verificação para isso.
Quando houver dados, a aplicação bloqueada será
acordada automaticamente.
Riscos
• Quando várias threads acessam o mesmo
recurso, como campos estáticos, variáveis de
instância você deve tomar cuidado para que
os dados estejam consistentes.
• Para isso, temos o recurso de sincronização,
a única maneira de garantir que duas threads
não acessem ao mesmo tempo determinado
recurso, corrompendo os dados.
Processo sequencial
• Somente uma rotina é
executada por vez
• As demais rotinas, ficam na
fila esperando
ExemploFor
package net.javabasico.thread;

public class ExemploFor {


public static void main(String[] args) {
for (int i = 1; i < 100000; i++) {
System.out.println(i);
}
System.out.println("Acabou o primeiro FOR");
for (int i = 1; i < 100000; i++) {
System.out.println(i);
}
System.out.println("Acabou o programa");
}
}
Execução
• Quando o segundo FOR começa?
• Enquanto isso eu fico esperando?
Vários processos
• Várias rotinas
podem rodar ao
mesmo tempo
• Esse multi-
processamento é
simulado
Multi-threading em Java
• Cada programa Java tem ao menos uma
thread
• Do ponto de vista do programador, você
inicia somente uma thread, chamada Main
Thread
• Essa thread tem a habilidade de criar
threads adicionais
Estados
• Novo - estado que uma thread fica no momento de sua instanciação, antes da
chamada do método start();
• Executável - estado em que a thread fica disponível para ser executada e no
aguardo do escalonador de thread, esperando a sua vez de se executar;
• Execução - Momento em que a thread está executando, está operando;
• Espera/Bloqueio/Suspensão - esse estado pode ser dar por inúmeros
motivos.
– Uma thread em sua execução pode se bloquear porque algum recurso ou
objeto não está disponível, por isso seu estado pode ficar bloqueado, até que
esse recurso/objeto esteja disponível novamente assim seu estado torna-se
executável, ou então, uma thread pode ficar suspensa porque o programador
definiu um tempo de espera, assim que esse tempo expirar essa thread volta
ao estado executável para continuar seus serviços;
• Inativo - a partir do momento em que o método run() foi concluído, a thread se
tornará inativa, porém ainda existirá o objeto na memória, somente não como
uma linha de execução, e não poderá novamente ser iniciada, ou seja,
qualquer tentativa de chamada do método start() após a conclusão do
métodos run(), uma exceção será lançada;
Estados de uma Thread em Java
nascimento
Término do tempo de dormida
start( )
Fim da E/S
notify( )
pronta
notifyAll( )
run( ) Alocar um processador

executando
wait( ) E/S
sleep( ) Fim do
Método run( )

esperando dormindo morta bloqueada


Prioridades de uma Thread
• Determina qual thread recebe o controle de
CPU e é executada primeiro
• Intervalo entre 1 e 10, onde 10 tem mais
chance de ser executada
• Entre duas threads com a mesma prioridade,
o sistema operacional decide qual será
executada
• O valor padrão é 5
Criando uma thread
• Você pode criar threads de duas maneiras
– Criando uma subclasse de Thread
– Implementando a interface Runnable e
criando o método run
Thread: alguns métodos
• public static Thread currentThread()
– Retorna a thread corrente
• public final String getName()
– Retorna o nome da thread
• public final void setName(String name)
– Atribui o nome para a thread
ExemploDeForThread
package net.javabasico.thread;

public class ExemploDeForThread extends Thread {


public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Número sequencial: " + i);
}
}
}
TestePrimeiroExemplo
package net.javabasico.thread;

public class TestePrimeiroExemplo {


public static void main(String[] args) {
ExemploDeForThread processo1 = new ExemploDeForThread();
processo1.start();
}
}
O que aconteceu?
• Alguma mudança perceptível?
TestePrimeiroExemplo
package net.javabasico.thread;

public class TestePrimeiroExemplo {


public static void main(String[] args) {
ExemploDeForThread processo1 = new ExemploDeForThread();
processo1.start();

ExemploDeForThread processo2 = new ExemploDeForThread();


processo2.start();
}
}
Concorrência
• Os dois processos estão concorrendo pelo
processador (só tem um!!!!)
• Quem decide é o sistema operacional
• Troque o valor do loop for de 1000 para 100
e veja o que acontece
Outros exemplos
• Existem várias maneiras de se conseguir o
mesmo resultado, mudando apenas a lógica
da aplicação.
• Vamos mudar um pouco a ordem dos
métodos e ver o resultado.
ImprimeNomeDaThread
package net.javabasico.threads;

public class ImprimeNomeDaThread extends Thread {


public ImprimeNomeDaThread(String nome) {
super(nome);
start();
}

@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(getName() + " - " + i);
}
}
}
TesteImprimeNome
package net.javabasico.threads;

public class TesteImprimeNome {


public static void main(String[] args) {
new ImprimeNomeDaThread("A");
new ImprimeNomeDaThread("B");
}
}
ExemploContador
public class ExemploContador {
public static void main(String args[]) {
ContadorThread c1 = new ContadorThread();
c1.setQtde(10);
c1.setName("t001");
c1.start();
ContadorThread c2 = new ContadorThread();
c2.setQtde(15);
c2.setName("t002");
c2.start();
}
}
Cont...
class ContadorThread extends Thread {
private int qtde = 0;
public void run() {
System.out.println("Exibindo múltiplos de " + qtde);
for (int i = 0; i <= 100; i++) {
if ((i % qtde) == 0) {
System.out.println(Thread.currentThread().getName() + "> " + i);
}
try {
sleep(50);
} catch (InterruptedException ex) {
}
}
}

public void setQtde(int value) {


this.qtde = value;
if (this.qtde == 0) this.qtde = 10;
}
}
Segunda alternativa
• Implemente a interface Runnable
• Crie o método run()
• Essa é uma alternativa mais limpa, mas
gasta uma linha a mais de código
ExemploDeForRunnable
package net.javabasico.thread;

public class ExemploDeForRunnable implements Runnable {


public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Número sequencial: " + i);
}
}
}
TestePrimeiroExemplo
package net.javabasico.thread;

public class TestePrimeiroExemplo {


public static void main(String[] args) {
ExemploDeForRunnable processo1 = new ExemploDeForRunnable();
Thread minhaThread = new Thread(processo1);
minhaThread.start();

ExemploDeForRunnable processo2 = new ExemploDeForRunnable();


minhaThread = new Thread(processo2);
minhaThread.start();

}
}
Recuperando o nome da thread
package net.javabasico.thread;

public class ExemploDeForRunnable implements Runnable {


public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Número sequencial: " + i);
System.out.println("Nome da Thread: "
+ Thread.currentThread().getName());
}
}
}
Nome da Thread
package net.javabasico.thread;

public class TestePrimeiroExemplo {


public static void main(String[] args) {
ExemploDeForRunnable processo1 = new ExemploDeForRunnable();
Thread minhaThread = new Thread(processo1);
minhaThread.setName("Thread 1");
minhaThread.start();

ExemploDeForRunnable processo2 = new ExemploDeForRunnable();


minhaThread = new Thread(processo2);
minhaThread.setName("Thread 2");
minhaThread.start();
}
}
Thread.sleep()
package net.javabasico.thread;

public class ExemploDeForRunnable implements Runnable {


public void run() {
try {
for (int i = 0; i < 1000; i++) {
System.out.println("Número sequencial: " + i);
System.out.println("Nome da Thread: "
+ Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Mudando a prioridade
package net.javabasico.thread;

public class TestePrimeiroExemplo {


public static void main(String[] args) {
ExemploDeForRunnable processo1 = new ExemploDeForRunnable();
Thread minhaThread = new Thread(processo1);
minhaThread.setName("Thread 1");

minhaThread.setPriority(1);
minhaThread.start();

ExemploDeForRunnable processo2 = new ExemploDeForRunnable();


minhaThread = new Thread(processo2);
minhaThread.setName("Thread 2");

minhaThread.setPriority(10);
minhaThread.start();
}
}
Vários outros exemplos
• Na sequência temos alguns exemplos de
como trabalhar com threads.
BarraDeProgresso
package net.javabasico.threads;

import javax.swing.*;

public class BarraDeProgresso extends Thread {


private int quantidadeDeSegundos;

public BarraDeProgresso(int segundos) {


quantidadeDeSegundos = segundos;
start();
}

@Override
public void run() {
try {
for (int i = 0; i < quantidadeDeSegundos; i++) {
Thread.sleep(1000);
System.out.print(".");
}
JOptionPane.showMessageDialog(null, "Tarefa concluída.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
TestaBarraDeProgresso
package net.javabasico.threads;

public class TestaBarraDeProgresso {


public static void main(String[] args) {
new BarraDeProgresso(10);
}
}
CalculadoraCientifica
package com.javabasico.thread;
public class CalculadoraCientifica extends Thread {
String universidade;
String separador;
public CalculadoraCientifica(String s, String separador) {
universidade = s;
this.separador = separador;
}
public void run() {
System.out.println("Comecando o calculo -> " + universidade);
for (int i = 0; i < 10; i++) {
try {
//Para a thread por um per’odo menor que 1s (aleatorio)
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
}
System.out.print(separador);
}
System.out.println("Terminei -> " + universidade);
}
}
Teste
package com.javabasico.thread;
public class CalculadoraCientificaTeste {
public static void main(String[] args) {
CalculadoraCientifica usp = new CalculadoraCientifica("USP", ".");
CalculadoraCientifica unb = new CalculadoraCientifica("UNB", ",");
try {
usp.start();
unb.start();
System.out.println("Threads iniciadas.");
//Espera ate a usp terminar.
usp.join();
System.out.println("A USP terminou.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Utilizando join
try {
usp.start();
unb.start();
System.out.println("Threads iniciadas.");
//Espera ate no maximo 1000ms a usp terminar.
//Se nao terminar a execucao continua.
usp.join(1000);
System.out.println("A USP deveria ter terminado.");
unb.join();
System.out.println("A UNB terminou.");
} catch (Exception e) {
e.printStackTrace();
}
Métodos estáticos
• Recursos compartilhados podem ter
problemas com acesso simultâneo.
• Para tanto, esses métodos devem ser
sincronizados conforme o exemplo.
UtilPropriedades
package net.javabasico.threads;

public class UtilPropriedades {


public static void imprime(String chave, String valor) {
try {
System.out.println("Chave: " + chave);
Thread.sleep(500);
System.out.println("Valor: " + valor);
System.out.println("--------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Propriedades
package net.javabasico.threads;

public class Propriedades extends Thread {


private String chave;
private String valor;

public Propriedades(String chave, String valor) {


this.chave = chave;
this.valor = valor;
start();
}

@Override
public void run() {
UtilPropriedades.imprime(chave, valor);
}
}
TestePropriedades
package net.javabasico.threads;

public class TestePropriedades {


public static void main(String[] args) {
new Propriedades("Nome", "Marco Antonio");
new Propriedades("Endereço", "Guará");
new Propriedades("Telefone", "3354-8689");
}
}
Sincronização
• Várias threas acessaram o mesmo recurso,
corrompendo as informações.
• Para resolver esse problema, utilizamos a
sincronização.
• Dessa forma, somente uma thread irá
acessar esse recurso por vez, garantindo que
os dados permaneçam corretos.
UtilPropriedades
package net.javabasico.threads;

public class UtilPropriedades {


public synchronized static void imprime(String chave, String valor) {
try {
System.out.println("Chave: " + chave);
Thread.sleep(500);
System.out.println("Valor: " + valor);
System.out.println("--------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Agendamento de tarefas
• O próximo exemplo é um relógio que executa
alguma tarefa em determinados intervalos de
tempo.
• Dessa forma, você pode executar tarefas
secundárias sem onerar sua aplicação.
Relogio
package net.javabasico.threads;

import java.sql.*;

public class Relogio extends Thread {


@Override
public void run() {
try {
while (true) {
Thread.sleep(1000);
System.out.println(new Time(System.currentTimeMillis()));
}
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {


new Relogio().start();
}
}
Agencia bancária
• Agora vamos simular um dia de expediente
bancário.
• Varias classes interagem com diversas
threads rodando na memória.
• O mesmo recurso (Banco) é acessado por
diversas origens.
• Uma situação típica onde os dados se
corrompem e devemos utilizar o recurso da
sincronização.
Banco
package com.javabasico.thread.banco;
public class Banco {
private static final int NUMERO_PARA_TESTE = 1000; //Implementacao futura
private int quantidadeDeTransacoes;
private int[] contas;
/**
* Contrutor da classe
* Recebe a quantidade de contas a serem criadas
* Inicia cada conta com o respectivo saldo
* Inicia a quantidade de transacoes do dia (zero)
* */
public Banco(int quantidadeDeContas, int saldoInicial) {
contas = new int[quantidadeDeContas];
for (int i = 0; i < contas.length; i++) {
contas[i] = saldoInicial;
}
quantidadeDeTransacoes = 0;
}
Banco
/**
* Metodo central da aplicacao
* Como parametro voce deve indicar a conta de origem, a conta de destino e o valor
*
* */
public void transfere(int origem, int destino, int valorParaTransferir) {
//Caso a conta de origem nao tenha saldo suficiente, para a execucao
if (contas[origem] < valorParaTransferir) {
// System.out.println("Saldo insuficiente");
//wait();
return;
}
contas[origem] -= valorParaTransferir;
contas[destino] += valorParaTransferir;
quantidadeDeTransacoes++;
//notifyAll();
//if (ntransacts % NTEST == 0) {
//verificaAtivosDoBanco();
//}
}
Banco
/**
* Varre todas as contas e verifica se o volume de dinheiro esta correto
* Os ativos do banco devem ser iguais durante toda a execucao do programa,
* uma vez que o dinheiro sai de uma conta e entra na outra.
* Nao pode nem aparecer e muito menos deparecer dinheiro.
*/
public void verificaAtivosDoBanco() {
int total = 0;
for (int i = 0; i < contas.length; i++) {
total += contas[i];
}
if (total != MovimentacoesDoDia.SALDO_INICIAL
* MovimentacoesDoDia.NUMERO_DE_CONTAS) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Quantidade de transacoes: " + quantidadeDeTransacoes
+ "\nTotal depositado: " + total);
}
Banco
public int size() {
return contas.length;
}
}
Transferencia
package com.javabasico.thread;

import com.javabasico.thread.banco.*;

public class ThreadDeTranferencia extends Thread {


private Banco bancoLocal;
private int contaDeOrigem;
private int contaDeDestino;

public ThreadDeTranferencia(Banco b, int from, int max) {


bancoLocal = b;
contaDeOrigem = from;
contaDeDestino = max;
}
Transferencia
@Override
public void run() {
try {
while (true) {
int contaDeDestinoAleatoria = (int) (bancoLocal.size() * Math.random());
int valorAleatorio = (int) (contaDeDestino * Math.random());
bancoLocal.transfere(contaDeOrigem, contaDeDestinoAleatoria, valorAleatorio);
sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Auditoria
package com.javabasico.thread.banco;
public class ThreadDeAuditoria extends Thread {
private Banco banco;
public ThreadDeAuditoria(Banco banco) {
this.banco = banco;
}
public void run() {
// Loop infinito
while (true) {
banco.verificaAtivosDoBanco();
try {
Thread.sleep(5 * 1000); //Verifica os ativos a cada 5 segundos
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("--------");
}
}
}
Movimentações
package com.javabasico.thread.banco;
import com.javabasico.thread.*;
public class MovimentacoesDoDia {
public static final int NUMERO_DE_CONTAS = 50;
public static final int SALDO_INICIAL = 100;
public static void main(String[] args) {
//Inicia um banco com N contas.
//atribui a cada conta o saldo inicial
Banco nossoBanco = new Banco(NUMERO_DE_CONTAS, SALDO_INICIAL);
for (int i = 0; i < NUMERO_DE_CONTAS; i++) {
ThreadDeTranferencia t = new ThreadDeTranferencia(nossoBanco, i,
SALDO_INICIAL);
t.start();
}
ThreadDeAuditoria t = new ThreadDeAuditoria(nossoBanco);
t.start();
}
}

Você também pode gostar