Java Threads
Multitarefa e Multithreading
• Multitarefa refere-se à capacidade de um
computador de executar vários trabalhos
simultaneamente
– mais de um programa em execução
simultaneamente, por exemplo, UNIX
Multitarefa e Multithreading
• Uma thread é uma única sequência de
execução dentro de um programa
• Multithreading refere-se a vários threads
de controle dentro de um único programa
– cada programa pode executar vários threads
de controle dentro dele, por exemplo,
navegador da Web
Concorrência vs. Paralelismo
CPU CPU1 CPU2
Threads e Processos
CPU
main
run
Process 1 Process 2 Process 3 Process 4
GC
Para que serve uma thread?
• Manter a capacidade de resposta de um
aplicativo durante uma tarefa longa.
• Permitir o cancelamento de tarefas
separáveis.
• Alguns problemas são intrinsecamente
paralelos.
• Monitorar o status de algum recurso (BD).
Aplicação de Thread
• Quando executamos uma aplicação:
– A JVM cria um objeto Thread cuja tarefa é
definida pelo método main()
– O thread executa as instruções do programa
uma por uma até que o método retorne e a
thread morra.
– Toda Thread é criada e controlada pela classe
java.lang.Thread
Multiplas Threads
• Cada thread tem sua pilha de execução privada
• Se duas threads executarem o mesmo método,
cada uma terá sua própria cópia das variáveis
locais que o método usa
• No entanto, todos os threads veem a mesma
memória dinâmica (heap)
• Duas threads diferentes podem atuar no mesmo
objeto e nos mesmos campos estáticos
simultaneamente
Criando Threads
• Existem duas maneiras de criar nosso
próprio objeto Thread:
– Subclassificando a classe Thread e
instanciando um novo objeto dessa classe
– Implementando a interface Runnable
• Em ambos os casos o método run() deve
ser implementado
Extends Thread
public class ThreadExemplo extends Thread {
public void run () {
for (int i = 1; i <= 100; i++) {
System.out.println(“Thread: ” + i);
}
}
}
Métodos Thread
void start()
– Cria uma nova thread e a torna executável
– Método só pode ser chamado apenas uma vez
void run()
– A nova thread começa sua vida dentro deste
método
void stop() (descontinuada)
- A thread é encerrada
Métodos Thread
• yield()
– Faz com que o objeto thread atualmente em
execução seja pausado temporariamente e
permita que outras threads sejam executadas
– Permitir que apenas threads da mesma
prioridade sejam executados
• sleep(int m)/sleep(int m,int n)
– A thread dorme por m milissegundos, mais n
nanossegundos
Implementing Runnable
public class RunnableExemplo implements Runnable {
public void run () {
for (int i = 1; i <= 100; i++) {
System.out.println (“Runnable: ” + i);
}
}
}
Um Object Runnable
• O método run() do objeto Thread chama o
método run() do objeto Runnable
• Permite que threads sejam executadas
dentro de qualquer objeto,
independentemente da herança
Iniciando as Threads
public class Main {
public static void main (String argv[]) {
new ThreadExemplo ().start ();
new Thread(new RunnableExemplo ()).start ();
}
}
Threads
start()
Fila prontos
Threads
recém
criada
Thread executada
I/O operation completes
•Esperand operação E/S ser completada
•Aguardando Notificação
•Dormindo
•Aguardando sincronização
Estados de uma Thread
• New
• Runnable
• Running
• Not Runnable
– Bloqueada para E/S
– Esperando variável de condição
– Sleeping (dormindo)
• Dead
Estados de uma Thread
Thread State Diagram
Alive
Executando
new ThreadExample(); while (…) { … }
Nova Thread Runnable Dead Thread
thread.start();
run() method returns
Blocked
Object.wait()
Thread.sleep()
Bloqueada IO call
Aguardando um monitor
Exemplo
public class PrintThread1 extends Thread {
String name;
public PrintThread1(String name) {
this.name = name;
}
public void run() {
for (int i=1; i<500 ; i++) {
try {
sleep((long)(Math.random() * 100));
} catch (InterruptedException ie) { }
System.out.print(name);
}}
Exemplo (cont)
public static void main(String args[]) {
PrintThread1 a = new PrintThread1("*");
PrintThread1 b = new PrintThread1("-");
PrintThread1 c = new PrintThread1("=");
a.start();
b.start();
c.start();
}
}
Agendamento
• O agendamento de threads é o mecanismo
usado para determinar como threads
executáveis são alocados no tempo de CPU
• Um mecanismo de escalonamento de
threads é preemptivo ou não preemptivo
Agendamento Preemptivo
• Agendamento preemptivo – o agendador de
threads antecipa (pausa) um thread em
execução para permitir a execução de
diferentes threads
• Agendamento não preemptivo – o
agendador nunca interrompe um thread em
execução
Agendamento Preemptivo
• O escalonador não-preemptivo depende da
thread em execução para ceder o controle
da CPU para que outras threads possam
executar
Starvation
• Um escalonador não-preemptivo pode
causar starvation (threads executáveis,
prontos para serem executados, aguardam
muito tempo para serem executados na
CPU, talvez até para sempre)
• Às vezes, a starvation também é chamada
de livelock
Agendamento interval de tempo
• Agendamento dividido em intervalos de
tempo – o agendador aloca um período de
tempo em que cada thread pode usar a
CPU
– quando esse período de tempo tiver
decorrido, o agendador interrompe o thread e
muda para um thread diferente
Agendamento intervalo de tempo
• Agendador sem divisão de tempo – o
agendador não usa o tempo decorrido para
determinar quando antecipar um thread
– ele usa outros critérios, como prioridade ou
status de E/S
Escalonador Java
• O escalonador é preemptivo e baseado na
prioridade dos threads
• Usa agendamento de prioridade fixa:
– Os threads são agendados de acordo com sua
prioridade em relação a outras threads na fila
pronta
Escalonamento Java
• A thread executável de maior prioridade é sempre
selecionado para execução acima dos threads de
prioridade mais baixa
• Quando várias threads têm prioridades igualmente altas,
é garantido que apenas um dessas threads esteja em
execução
• Threads Java são garantidas como preemptivos, mas não
com divisão de tempo
Prioridade Thread
• Cada thread tem uma prioridade
• Quando uma thread é criada, ela
herda a prioridade da thread que o
criou
• Os valores de prioridade variam de 1 a
10, em prioridade crescente
Thread Priority (cont.)
• A prioridade pode ser ajustada posteriormente
usando o método setPriority()
• A prioridade de uma thread pode ser obtida
usando getPriority()
• Constantes de prioridade são definidas:
– MIN_PRIORITY=1
– MAX_PRIORITY=10
– NORM_PRIORITY=5
Curiosidade
• A implementação de thread em Java é
baseada no suporte do SO.
• Alguns SOs Windows suportam apenas 7
níveis de prioridade, portanto, diferentes
níveis em Java podem, na verdade, ser
mapeados para o mesmo nível de sistema
operacional
Executor
• Framework utilizado para tarefas
concorrentes
• Executor executa Runnables, criando e
gerenciando um grupo de threads
denominado de pool de threads
• A interface executor declara um método
denominado execute que aceita Runnable
como argumento
Executor
• Vantagens na reutilização de threads
existentes, eliminando sobrecarga ao criar
uma nova thread
• Melhora o desempenho otimizando o
número de threadsa fim de ocupar o
processador
public class PrintTask implements Runnable {
private static final SecureRandom generator = new SecureRandom();
private final int sleepTime; // tempo de adormecimento aleatório para a thread
private final String taskName;
// construtor
public PrintTask(String taskName)
{
this.taskName = taskName;
// seleciona tempo de adormecimento aleatório entre 0 e 5 segundos
sleepTime = generator.nextInt(5000); // milissegundos
}
// método run contém o código que uma thread executará
public void run(){
try // coloca a thread para dormir pela quantidade de tempo sleepTime
{
System.out.printf("%s indo dormir por %d milliseconds.%n",taskName, sleepTime);
Thread.sleep(sleepTime); // coloca a thread para dormir
}
catch (InterruptedException exception)
{
exception.printStackTrace();
Thread.currentThread().interrupt(); // reinterrompe a thread
}
// imprime o nome da tarefa
System.out.printf("%s acordada %n", taskName);
}
}
InterruptedExceptions
• É uma boa prática permitir que thread
em execução trate a exceção
InterruptedExceptions
ExecutorService
• Gerenciando threads com o método
Executors newCachedThreadPool para
obter um ExecutorService que é capaz
de criar novas threads, à medida que
são necessárias, pelo aplicativo.
• Essas threads são usadas pelo
ExecutorService para executar
Runnables.
public class Main {
public static void main (String [] args) {
PrintTask pt1 = new PrintTask("primeira");
PrintTask pt2 = new PrintTask("segunda");
PrintTask pt3 = new PrintTask("terceira");
System.out.println("Iniciando Executor");
ExecutorService executorService =Executors.newCachedThreadPool();
executorService.execute(pt1);
executorService.execute(pt2);
executorService.execute(pt3);
//fecha ExecutorService - ele decide quando fechar as threads
//parar de aceitar novas tarefas, mas continua executando as tarefas que já foram
submetidas.
executorService.shutdown();
System.out.println("Tarefas finalizadas");
}
}
ExecutorService
• Repare que as threads terminam depois
da main.
• Apesar da main ter sido encerrada, ela
somente terminará após o final da
execução das threads
ExecutorService
• Reparem nas ordens das threads
não podemos prever a ordem em que as
tarefas começarão a ser executadas,
mesmo se conhecermos a ordem em que
elas foram criadas e iniciadas.
Threads Perigosa
• Veja o exemplo da thread perigosa
Sincronização Threads
Threads que compartilham e modificam um
objeto (dados) pode ter resultados
indeterminados.
É necessário uma sincronização das threads
Somente uma thread por vez pode acessar
exclusivamente um objeto compartilhado.
Sincronização Threads
Exclusão mútua permite que apenas uma
thread tenha acesso exclusivo a um
objeto compartilhado.
Condição Corrida
Uma condição de corrida – o resultado de um
programa é afetado pela ordem em que os
threads do programa são alocados no tempo
de CPU
Dois threads estão modificando
simultaneamente um único objeto
Ambos os threads “correm” para armazenar
seu valor
Exemplo Condição Corrida
Coloca peças
Como alternar Coloca peças
verdes as cores vermelhas
Monitores
Cada objeto possui um “monitor” que é um token
usado para determinar qual thread do aplicativo
tem controle de uma instância específica do
objeto.
Na execução de um método (ou bloco)
sincronizado, o acesso ao monitor do objeto deve
ser obtido antes da execução
O acesso ao monitor de objetos é enfileirado
Monitores
Monitores são predefinidos em Java para
garantir o sincronização.
Cada objeto tem um bloqueio de monitor (
bloqueio intrínseco)
Monitor garante que o bloqueio do seu
objeto é mantido por no máximo uma
única thread por vez.
Monitores
Entrar em um monitor também é conhecido
como bloquear o monitor ou adquirir a
propriedade do monitor
Se um thread A tentar adquirir a
propriedade de um monitor e um thread
diferente já tiver entrado no monitor, o
thread atual (A) deverá esperar até que o
outro thread saia do monitor.
Monitores
syncronized (objeto)
{
}
Objeto é o que deve ser bloqueado pelo
monitor. Normalmente é o this. Caso
haja vários
Exemplo
public class ContaBancaria {
private float balanco;
public synchronized void deposita(float valor) {
balanco += valor;
}
public synchronized void saca(float valor) {
balanco -= valor;
}
}
t3 t2 t1 Seção Crítica
deposita()
Conta Bancária
Deadlock
public class ContaBancaria {
private float balanco;
public synchronized void deposita(float valor) {
balance += valor;
}
public synchronized void saca (float valor) {
balance -= valor;
}
public synchronized void transfere(float valor,
ContaBancaria destino) {
saca(valor);
destino.deposita(valor);
}
}
public class Transferencia implements Runnable {
private ContaBancaria origem, destino;
private float valor;
public transfere(ContaBancaria origem, ContaBancaria
destino, float valor) {
this.origem = origem;
this.destino = destino
this.valor = valor;
}
public void run() {
origem.transfere(valor, destino);
}
}
ContaBancaria anton = new ContaBancaria();
ContaBancaria karl = new ContaBancaria();
...
// em algum lugar
Runnable transaction1 =
new Transferencia(anton, karl, 1200);
Thread t1 = new Thread(transaction1);
t1.start();
// em outro lugar
Runnable transaction2 =
new Transferencia(karl, anton, 700);
Thread t2 = new Thread(transaction2);
t2.start();
Deadlocks
t1 t2
anton karl
transfere() transfere()
?
saca() saca()
deposita() deposita()
Bloqueios Java são reentrantes
Qual é o problema com o código abaixo
public class Test {
public synchronized void a() {
b();
System.out.println(“Passando em a”);
}
public synchronized void b() {
System.out.println(“Passando em b”);
}
}
Declarações Sincronizadas
Um monitor pode ser atribuído a um bloco
Pode ser usado para monitorar o acesso a um
elemento de dados que não é um objeto, por
exemplo, array
Exemplo:
void arrayShift(byte[] array, int count) {
synchronized(array) {
System.arraycopy (array, count,array, 0, array.size - count);
}
}
Synchronization Thread
Precisamos sincronizar as transações, por
exemplo, o cenário consumidor-produtor
Wait and Notify
Permite que duas threads cooperem
Baseado em um único objeto de bloqueio
compartilhado
Marge colocou um cookie, espere e avise Homer
Homer come um biscoito, espera e avisa Marge
Marge colocou um cookie, espere e avise Homer
Homer come um biscoito, espera e avisa Marge
O método wait()
O método wait() faz parte da interface
java.lang.Object
Requer um bloqueio no monitor do objeto
para ser executado
O método wait()
wait() faz com que o thread atual espere até
que outro thread invoque o método notify()
ou o método notifyAll() para este objeto
Ao chamar wait(), o thread libera a
propriedade deste monitor e espera até que
outro thread notifique as threads em
espera do objeto
O método wait()
wait() também é semelhante a yield()
Ambos retiram a thread atual da pilha de
execução e forçam seu reagendamento
No entanto, wait() não é automaticamente
colocado de volta na fila do agendador
notify() deve ser chamado para colocar um thread
de volta na fila do agendador
Consumidor
synchronized (lock) {
while (!resourceAvailable()) {
lock.wait();
}
consumeResource();
}
Produtor
produceResource();
synchronized (lock) {
lock.notifyAll();
}
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produzRecurso()
2. lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
6.}
10. } 7. Readquire lock
8. Retorna do wait()
Thread
Thread
Produtora
Consumidora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produzRecurso()
2. lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
10. } 6.}
7. Readquire lock
8. Retorna do wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produzRecurso()
2. lock.wait(); 4. synchronized(lock) {
9. consomeRecurso(); 5. lock.notify();
10. } 6.}
7. Readquire lock
8. Retorna do wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
2. lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
10. 6.}
10. }} 7. Readquire
Reacquire lock
8.
8. Retorna
Return from
do wait()
wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
consumeResource();
10. 6.}
10. }} 7. Readquire
Reacquire lock
8. Retorna
8. Return from wait()
do wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
consumeResource();
10. 6.}
10. }} 7. Readquire
Reacquire lock
8.
8. Retorna
Return from
do wait()
wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
consumeResource();
10. 6.}
10. }} 7. Readquire
Reacquire lock
8.
8. Retorna
Return from
do wait()
wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
consumeResource();
10. 6.}
10. }} 7. Reacquire lock
8.
8. Retorna
Return from
do wait()
wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
consumeResource();
10. 6.}
10. }} 7. Readquire
Reacquire lock
8. Return from wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consumeResource();
10. 6.}
10. }} 7. Readquire
Reacquire lock
8.
8. Retorna
Return from
do wait()
wait()
Thread Thread
Consumidora Produtora
Sequência Wait/Notify
Lock Object
1. synchronized(lock){ 3. produceResource()
produzRecurso()
2. lock.wait();
lock.wait(); 4. synchronized(lock) {
5. lock.notify();
9. consomeRecurso();
consumeResource();
6.}
10. } 7. Readquire
Reacquire lock
8.
8. Retorna
Return from
do wait()
wait()
Thread Thread
Consumidora Produtora