Sincronização de Threads

Propaganda
Si
ncroni
zaçã
o de Threads
Si
s
tema j
us
to
quando cada threadobtém aces
s
os
ufici
ente a recurs
osl
i
mi
tadosa
fim de prog
redi
r razoavel
mente
Starvati
on
ocorre quando uma ou mai
sthreadses
tã
oi
mpedi
dasde obter
aces
s
o a um recurs
o, nã
o podendo, cons
equentemente, prog
redi
r
Deadl
ock
Starvati
on ao má
xi
mo:
ocorre quando duasou mai
sthreadses
tã
oà
es
pera numa condi
çã
o que nã
o pode s
er s
ati
s
fei
ta
Sincronização de Threads
Exemplo do Produtor/
Consumidor
Produtor:
public class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" + this.number
+ " put: " + i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
Sincronização de Threads
Consumidor:
public class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumer #" + this.number
+ " got: " + value);
}
}
}
Sincronização de Threads
Nem o produtor nem o consumidor se esforçam para que o
consumidor obtenha apenas 1 valor produzido uma única vez
A sincronização ocorre a baixo ní
vel, nos métodos put(
)e get(
)do
CubbyHole
Problema #1: o produtor é mais rápido que o consumidor:
produtor gera 2 números antes que o consumidor consiga um
Consumer
Producer
Producer
Consumer
#1
#1
#1
#1
got:
put:
put:
got:
3
4
5
5
Problema #2: o consumidor é mais rápido que o produtor:
consome o mesmo valor duas vezes
Producer
Consumer
Consumer
Producer
#1
#1
#1
#1
put:
got:
got:
put:
4
4
4
5
Produtor/Consumidor
Copie de http://java.
sun.
com/docs/books/tutorial/essential/
threads/example/ProducerConsumerTest.
java ,o programa
que cria um objecto CubbyHole, um Produtor, um
Consumidor e lança o produtor e consumidor
Verifique o seu output
Produtor/Consumidor
public class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
o consumidor obtém o trinco do CubbyHole
...
}
o consumidor liberta o trinco do CubbyHole
public synchronized void put(int value) {
o produtor obtém o trinco do CubbyHole
...
}
}
o produtor liberta o trinco do CubbyHole
notifyAll() e wait()
O método wait() larga o trinco mantido pelo consumidor no
CubbyHole (permitindo assim que o produtor readquira o
trinco) e fica à espera de uma notificação por parte do
produtor
O método notifyAll() acorda todas as threads em espera no
objecto em causa (neste caso, o CubbyHole).
As threads acordadas competem pelo trinco
Uma dessas threads obtém o trinco, as outras voltam a esperar
A classe Object também define o método notify(), o qual acorda
arbitrariamente uma das threads em espera sobre esse objecto
Semáforos
Introduzidos em 1965 por Dijkstra
Semáforos são inteiros não-negativos que apenas podem ser
acedidos por duas operações:
P() (ou wait() ou Esperar)
if (sem > 0) { sem = sem-1 } else { block until sem > 0 }
V() (ou signal() ou Assinalar)
sem = sem+1
P() e V() devem ser atómicos
Semáforos em Java
public class Semaphore {
private int count = 0;
public Semaphore(int initVal) {
count = initVal;
}
public synchronized void P() {
if (count <= 0)
try {
/* esperar até que count > 0 */
wait();
} catch (InterruptedException e) { }
count--;
}
public synchronized void V() {
count++;
notify(); /* acordar quem estiver em espera */
}
}
Receita para sincronização em Java
Usar variáveis (p.e. booleanas) para definir as condições
apropriadas
Proteger todas as variáveis de condição com métodos public
synchronized
Se a chamada a um método tem de ser bloqueada devido a
uma condição não ser verdadeira, a thread chamante faz
wait()
Se a chamada a um método torna uma condição sobre a
qual outras threads possam estar à espera verdadeira, a
thread chamante faz notify() ou notifyAll()
Exemplo: BoundedBuffer
class BoundedBuf {
private Vector buf = new Vector();
private Semaphore mutex = new Semaphore(1);
private Semaphore space_avail = new Semaphore(100);
private Semaphore item_avail = new Semaphore(0);
public void put(int item) {
space_avail.P();
mutex.P();
buf.addElement(item);
mutex.V();
item_avail.V();
}
public int get() {
item_avail.P();
mutex.P();
int item = buf.removeElementAt(0);
mutex.V();
space_avail.V();
return item;
}
}
Exercícios
Considere um parque de estacionamento. Programe em Java
a classe carParkControl, que gere as entradas e saídas de
carros (métodos depart() e arrive()). A capacidade do
parque é dada como parâ
metro. As classes Arrivals e
Departures efectuam a entrada e saída de um carro.
Exercícios
Considere um banco com uma só conta. Nessa conta é
possível efectuar as operações de depositar e levantar. Cada
uma destas operações tem um parâmetro inteiro com o
valor a depositar ou a levantar. A operação de depositar
consiste unicamente em somar o valor ao saldo actual. A
operação de levantar verifica se o saldo é suficiente para
satisfazer o levantamento e em caso afirmativo subtrai o
valor ao saldo. Considere ainda que ambas as operações
podem ser efectuadas por vários processos em simultâneo.
Escreva o código em Java das operações depositar e levantar.
Elabore sobre a necessidade de sincronização entre os vários
processos que executam as operações.
Download