Sincronização de Threads Sistema justo quando cada threadobtém ace so suficiente a recursoslimitadosa fim de progredir razoavelmente Starvation ocorre quando uma ou maisthreadsestão impedidasde obter ace so a um recurso, não podendo, consequentemente, progredir Deadlock Starvation ao máximo:ocorre quando duasou maisthreadsestão à espera numa condição que não pode ser satisfeita
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 #1 got: 3 Producer #1 put: 4 Producer #1 put: 5 Consumer #1 got: 5 Problema #2: o consumidor é mais rápido que o produtor: consome o mesmo valor duas vezes Producer #1 put: 4 Consumer #1 got: 4 Consumer #1 got: 4 Producer #1 put: 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 re-adquira 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.