Sincronização de Threads
Professor: Hyggo Almeida
Laboratório de Sistemas
Embarcados e Computação
Pervasiva
Centro de Engenharia
Elétrica e Informática
O que vimos na última aula?
Threads


Ciclo de vida
Escalonamento
Sincronização de Threads
2
O que veremos hoje?
Threads

Sincronização
Sincronização de Threads
3
Sincronização de Threads
As threads vistas até agora são executadas paralelamente
 Mas não há recursos compartilhados
 Elas são independentes
 Não se concorre por recursos...
 ...não há concorrência!!!
Sincronização de Threads
4
Sincronização de Threads
Produtor/Consumidor
 Um produtor gera um número entre 0 e 9 e o armazena
em uma instância de SharedResource
 O produtor dorme durante um intervalo aleatório de 0 a
100 ms antes de gerar mais números
 O consumidor consome os números inteiros acessando a
mesma instância de SharedResource
Sincronização de Threads
5
Sincronização de Threads
Produtor
public class Producer extends Thread {
private SharedResource resource;
private int number;
public Producer(SharedResource c, int number) {
resource = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
resource.put(i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
Sincronização de Threads
6
Sincronização de Threads
Produtor
public class Consumer extends Thread {
private SharedResource resource;
private int number;
public Consumer(SharedResource c, int number) {
resource = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = resource.get();
}
}
}
Sincronização de Threads
7
Sincronização de Threads
Produtor/Consumidor
 O produtor e o consumidor compartilham dados da
instância de SharedResource
 O consumidor deve receber um dado número apenas
uma vez...
 ... mas quem está controlando isso?
Sincronização de Threads
8
Sincronização de Threads
Produtor/Consumidor
 Uma vez que não há sincronização na execução das
threads, pode-se ter duas situações indesejadas:



O consumidor perde um número... caso o produtor
produza mais rápido um novo número antes que seja
consumido...
O consumidor consome números repetidos... caso o
produtor não produza um novo número a tempo
Esse tipo de problema é denominado condição de
corrida
Sincronização de Threads
9
Sincronização de Threads
Condição de corrida
 Quando duas ou mais threads ou processos
compartilham dados e o resultado final depende do
escalonamento
As atividades do produtor e do consumidor devem ser
sincronizadas em dois passos...


Travando o objeto (locking), impedindo que duas threads o acessem
ao simultaneamente
Fazer com que cada thread coordene seu trabalho, notificando a
outra thread quando o número foi produzido e consumido
Sincronização de Threads
10
Sincronização de Threads
Sincronização via locking


Trechos do código que possuem estruturas de dados acessadas por
threads diferentes e concorrentes são chamados regiões ou
seções críticas
Uma região crítica é demarcada pela palavra synchronized
public class SharedResource {
private int contents;
private boolean available = false;
public synchronized int get() {
...
}
public synchronized void put(int value) {
...
}
}
Sincronização de Threads
11
Sincronização de Threads
Sincronização via locking

Bloco “avulso” de código
//..
synchronized {
//qualquer trecho de código
}
//..
synchronized (obj){
//qualquer trecho de código
}
Sincronização de Threads
12
Sincronização de Threads
Sincronização via locking

Locking reentrante
public class Reentrant {
public synchronized void a() {
b();
System.out.println(“Estou em a()”);
}
public synchronized void b() {
System.out.println(“Estou em b()”);
}
}
Sincronização de Threads
13
Sincronização de Threads
Sincronização via wait e notifyAll
public synchronized int get() {
while (available == false) {
try {
//esperar o produtor avisar que produziu
wait();
} catch (InterruptedException e) { }
}
available = false;
//notificar produtor de que o valor foi recuperado
notifyAll();
return contents;
}
Sincronização de Threads
14
Sincronização de Threads
Sincronização via wait e notifyAll
public synchronized void put(int value) {
while (available == true) {
try {
//Esperar aviso do consumidor de que recuperou número
wait();
} catch (InterruptedException e) { }
}
contents = value;
available = true;
//Notificar consumidor de que número foi produzido
notifyAll();
}
Sincronização de Threads
15
Sincronização de Threads
Sincronização via wait e notifyAll

O método wait libera o lock e espera
notificação para continuar



Para que outra thread possa adquirir o lock, fazer
seu trabalho e então “acordar” a original (com
notifyAll)
Ao acordar, tem-se o lock novamente
O método notifyAll “acorda” todas as
threads que estão em wait (nesse objeto)


As threads que acordam competem pelo lock
Quando uma thread tem o lock, as outras
dormem
Sincronização de Threads
16
Sincronização de Threads
Sincronização via wait e notifyAll



Há também o método notify (escolha
arbitrária)
Só se pode usar wait(), notify() e notifyAll()
quando se está de posse do lock do objeto
(dentro de um bloco synchronized)
wait() espera uma condição para o objeto
corrente e esta condição ocorre com notify()
no mesmo objeto



wait()
wait(milisegundos)
wait(milisegundos, nanosegundos)
Sincronização de Threads
17
Sincronização de Threads
Vamos implementar o exemplo do
produtor/consumidor...
Sincronização de Threads
18
Sincronização de Threads
Locks explícitos e variáveis condicionais


Pode-se proteger regiões críticas com locks explícitos
 Permite proteger alguns statements
 Permite proteger múltiplos métodos
Para criar um lock explícito, instancia-se uma
implementação da interface Lock
 Em geral, ReentrantLock
Sincronização de Threads
19
Sincronização de Threads
Locks explícitos e variáveis condicionais



Para obter o lock, utiliza-se o método lock()
Para liberar... unlock()
Uma vez que o lock não é liberado automaticamente,
deve-se usar try...finally para garantir sua liberação
Lock aLock = new ReentrantLock();
…
aLock.lock();
try{
\\…
}finally{
aLock.unlock();
}
Sincronização de Threads
20
Sincronização de Threads
Locks explícitos e variáveis condicionais


Para esperar por um lock explícito, cria-se uma variável
condicional
 Um objeto que implementa a interface Condition
 Usar Lock.newCondition() para criar uma
condição
A condição provê métodos:
 await() para esperar até a condição ser verdadeira
 signal() e signalAll() para avisar os threads
que a condição ocorreu
Sincronização de Threads
21
Sincronização de Threads
Locks explícitos e variáveis condicionais

Variações de await()





await () - Espera uma condição ocorrer.
awaitUninterruptibly() - Espera uma condição ocorrer. Não pode ser
interrompido.
awaitNanos(long timeout) - Espera uma condição ocorrer. Espera no
máximo timeout nanossegundos.
await(long timeout, TimeUnit unit) - Espera uma condição ocorrer. Espera
no máximo timeout TimeUnit.
await(Date timeout) - Espera uma condição ocorrer. Espera no máximo até a
data especificada.
Sincronização de Threads
22
Sincronização de Threads
Locks explícitos e variáveis condicionais

Exemplo da classe SharedResource
import java.util.concurrent.locks.*;
public class SharedResource {
private int contents;
private boolean available = false;
private Lock aLock = new ReentrantLock();
private Condition condVar = aLock.newCondition();
//…
}
Sincronização de Threads
23
Sincronização de Threads
Locks explícitos e variáveis condicionais
 Exemplo
da{classe SharedResource
public
int get()
aLock.lock();
try {
while (available == false) {
try {
condVar.await(); //Esperar aviso de produção
} catch (InterruptedException e) { }
}
available = false;
//Notificar produtor de que número foi consumido
condVar.signalAll();
} finally {
aLock.unlock();
return contents;
}
}
Sincronização de Threads
24
Sincronização de Threads
Locks explícitos e variáveis condicionais
public
void put(int
value)
{
 Exemplo
da classe
SharedResource
aLock.lock();
try {
while (available == true) {
try {condVar.await();//Esperar aviso de que foi consumido
} catch (InterruptedException e) { }
}
contents = value;
available = true;
//Notificar consumidor de que número foi produzido
condVar.signalAll();
} finally {
aLock.unlock();
}
}
}
Sincronização de Threads
25
Sincronização de Threads
Estruturas de dados sincronizadas

Em vez de construir estruturas sincronizadas como o
SharedObject, pode-se utilizar algumas pré-definidas


Classes do pacote java.util.concurrent
Exemplo: BlockingQueue

Fila que trata de todos os detalhes de sincronização
Sincronização de Threads
26
Sincronização de Threads
Sincronizando coleções

As coleções de Java em geral (ArrayList, ...) não são
sincronizadas (não são thread-safe)


Exceção é Vector
Para criar coleções sincronizadas, deve-se criar um
decorador da coleção que sincroniza os métodos

Java já fornece tais decoradores na classe Collections






Collections.synchronizedCollection()
Collections.synchronizedList()
Collections.synchronizedMap()
Collections.synchronizedSet()
Collections.synchronizedSortedMap()
Collections.synchronizedSortedSet()
Sincronização de Threads
27
Sincronização de Threads
Sincronizando coleções

Apenas a lista decorada deve ser usada a partir de
então...
List list = Collections.synchronizedList(new ArrayList());

Iterações devem ser sincronizadas...
synchronized(list) {
Iterator i = list.iterator(); // Deve estar dentro do bloco
while (i.hasNext())
foo(i.next());
}
Sincronização de Threads
28
Sincronização de Threads
Starvation e deadlock...

Várias threads competem por recursos...
 Equidade (fairness) existe quando cada thread recebe
recursos suficientes para progredir
 Um sistema com equidade deve evitar starvation e
deadlock


Starvation ocorre quando uma ou mais
threads não conseguem obter recursos para
progredir
Deadlock é uma starvation que ocorre
quando as threads esperam por uma
condição que nunca vai ocorrer
Sincronização de Threads
29
Sincronização de Threads
Starvation e deadlock...

Exemplo clássico: filósofos!






Cinco filósofos estão sentados numa mesa redonda
Na frente de cada filósofo, há uma tijela de arroz
Entre cada dois filósofos há um pauzinho chinês
Para poder comer um bocado de arroz, um filósofo
deve ter 2 pauzinhos: o da esquerda e da direita
Os filósofos devem achar uma forma de compartilhar
pauzinhos de forma a que todos possam comer
Applet:

http://dsc.ufcg.edu.br/~jacques/cursos/map/html/threads/sincronizacao.html
Sincronização de Threads
30
O que vimos hoje?
Threads

Sincronização
Sincronização de Threads
31
O que veremos na próxima aula?
Threads
 Pool
Sincronização de Threads
32
Dúvidas?
?
Sincronização de Threads
33
Download

19.SincronizacaoDeThreads