0% acharam este documento útil (0 voto)
2 visualizações75 páginas

Threads Java IFSP

O documento aborda conceitos fundamentais sobre threads em Java, incluindo multitarefa, multithreading, e a diferença entre concorrência e paralelismo. Ele detalha como criar e gerenciar threads, os estados das threads, agendamento, sincronização e o uso de monitores para evitar condições de corrida. Além disso, discute o uso de ExecutorService para gerenciar tarefas concorrentes e a importância da sincronização para evitar deadlocks.

Enviado por

jaiane.s
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
2 visualizações75 páginas

Threads Java IFSP

O documento aborda conceitos fundamentais sobre threads em Java, incluindo multitarefa, multithreading, e a diferença entre concorrência e paralelismo. Ele detalha como criar e gerenciar threads, os estados das threads, agendamento, sincronização e o uso de monitores para evitar condições de corrida. Além disso, discute o uso de ExecutorService para gerenciar tarefas concorrentes e a importância da sincronização para evitar deadlocks.

Enviado por

jaiane.s
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
Você está na página 1/ 75

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

Você também pode gostar