Sei sulla pagina 1di 19

I Thread pi in dettaglio

se mai doveste usarli, ora sapete dove documentarvi!

Laboratorio di sistemi e reti


A.A. 2010-2011 Simone Bassis bassis@dsi.unimi.it

Corso di laurea in Comunicazione Digitale

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Come si istanzia un Thread


1. Si dichiara una classe come sottoclasse di Thread class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } e poi si invoca il metodo start() che schedula il Thread, in esecuzione verr eseguito il suo metodo run()

PrimeThread p = new PrimeThread(143); p.start();


Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

263

Come si istanzia un Thread


2. Si dichiara una classe che implementa linterfaccia Runnable class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } Perch mai si usa?

e poi come prima

PrimeRun p = new PrimeRun(143); new Thread(p).start();


Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

1. Devo implementarla ogni volta che un task (ovvero un oggetto) deve essere eseguito da un thread 2. Cos facendo possi estendere altre classi
264

Thread e Risorse condivise


o Sono il punto di forza dei thread
o Ma le insidie non mancano

o Come gestire la sincronizzazione?


o in passato usando le primitive
Synchronized: determina laccesso esclusivo ad una porzione di codice o a un metodo Wait: sospende lesecuzione di un Thread finch questo non viene svegliato Notify (NotifyAll): risveglia un thread dormiente
Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

265

Es. Produttore Consumatore


public class Producer extends Thread { private CubbyHole cubbyhole; private int number; // per chi non lo sapesse // CubbyHole = sgabuzzino, ripostiglio

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) { } } } }

Mai usare costanti in questo modo. Meglio definire delle variabili di istanza, piuttosto

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

266

Es. Produttore Consumatore


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); try { sleep((int) (Math.random() * 100)); } catch (InterruptedException e) { } } } }

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

267

Es. Produttore Consumatore


public class ProducerConsumerTest { public static void main(String[] args) { CubbyHole c = new CubbyHole(); Producer p1 = new Producer(c, 1); Consumer c1 = new Consumer(c, 1); p1.start(); c1.start(); } } public class CubbyHole { private int contents; synchronized public int get() { return contents; } synchronized public void put(int value) { contents = value; } }

Se la risorsa non viene condivisa in modo sincronizzato, si possono avere problemi di consistenza

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

268

Es. Produttore Consumatore


public class CubbyHole { private int contents; private boolean available = false; public synchronized int get() { while (available == false) { try { wait(); } catch (InterruptedException e) { } } available = false; notifyAll(); return contents; } public synchronized void put(int value) { while (available == true) { try { wait(); } catch (InterruptedException e) { } } contents = value; Attenzione: un ripostiglio available = true; pu essere pieno o vuoto! notifyAll(); }
Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

269

Esercizio 24
Si modifichi il codice desempio del problema produttore/ consumatore in modo tale da considerare una capacit massima del ripostiglio pari a size, propriet del ripostiglio. Si testi successivamente il sistema con un produttore e diversi consumatori. Per chi volesse, potrebbe contare il numero di volte che un oggetto viene messo nel (preso dal) ripostiglio, per poi stampare allutente una statistica di utilizzo.

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

270

Gestione concorrenza i semafori


o
o permettono di dare, o meno, l'accesso a una risorsa condivisa o garantendo mutua esclusione o accesso condiviso a un numero limitato di clienti

Sono una delle pi semplici strutture di controllo di concorrenza

o lo implementate per vostro conto o usate la classe Semaphore

Due alternative

o il richiedente si pone in attesa sul metodo acquire() o fino a che una risorsa non viene rilasciata da un altro client (con il metodo release())

Caratteristiche di Semaphore (java.util.concurrent)

avete a disposizione altri interessanti metodi, come il tryAcquire() dove il richiedente si pone in attesa per un limite di tempo e poi esce (senza acquisire la risorsa)

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

271

Es. Party
Esempio. La risorsa condivisa una festa (con un numero di posti liberi limitato) e un numero di invitati maggiore. Alcuni invitati, per pigrizia, attendono l'ingresso ma non oltre un certo tempo, dopodich lasciano perdere e si allontanano.

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

272

Es. Party
public class Party { //Dimensione massima della sala: quante persone al massimo riusciamo a servire int dimensione; //Il semaforo che controller gli accessi Semaphore semaphore; public Party(int dim){ this.dimensione = dim; //creiamo il semaforo con il numero massimo di accessi this.semaphore = new Semaphore(dimensione); } public boolean goIn(){ try{ semaphore.acquire(); if (semaphore.availablePermits()==0) System.out.println("Please wait... nessun posto disponibile!"); return true; }catch(InterruptedException e){ e.printStackTrace(); } return false; } public boolean goIn(long time){ try{ boolean in = semaphore.tryAcquire(time, TimeUnit.SECONDS); if (semaphore.availablePermits()==0) System.out.println("Please wait... nessun posto disponibile!"); return in; }catch(InterruptedException e){ e.printStackTrace(); } return false; } public void goOut(){ semaphore.release(); System.out.println("Uno meno! Posti liberi... "+semaphore.availablePermits()); 273 }

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Es. Party
public class Invited extends Thread { String name; Party party; public Invited(String name, Party party){ this.name = name; this.party = party; } //Facciamo un ciclo infinito di ingressi, attese, uscite, attese, ... public void run(){ while(true){ System.out.println("["+name+": ] Sono in coda. Aspetto."); party.goIn(); System.out.println("["+name+": ] Yuppy! Sono dentro."); try{ //Si diverte un po' alla festa int timeToSleep = (int) (Math.random()*10); System.out.println("["+name+": ] rimarr "+timeToSleep+" secs."); TimeUnit.SECONDS.sleep(timeToSleep); }catch(InterruptedException e){ e.printStackTrace(); } //decide di uscire... party.goOut(); System.out.println("["+name+": ] Esco a prendere aria."); try{ //sta un po' fuori... int timeToSleep = (int) (Math.random()*10); TimeUnit.SECONDS.sleep(timeToSleep); }catch(InterruptedException e){ e.printStackTrace(); } } } 274

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Es. Party
public class LazyInvited extends Invited { public LazyInvited(String name, Party party) { super(name, party); } //Facciamo un ciclo infinito di ingressi (con attesa limitata), attese, uscite, attese public void run(){ while(true){ System.out.println("["+name+": ] Sono in coda. Aspetter un po' (5 sec)."); boolean in = party.goIn(5); if (in){ System.out.println("["+name+": ] Yuppy! Sono dentro."); try{ //Si diverte un po' alla festa int timeToSleep = (int) (Math.random()*10); System.out.println("["+name+": ] rimarr "+timeToSleep+ secs."); TimeUnit.SECONDS.sleep(timeToSleep); }catch(InterruptedException e){ e.printStackTrace(); } //decide di uscire... party.goOut(); System.out.println("["+name+": ] Esco a prendere aria."); }else{ //timeout di attesa scaduto! System.out.println("["+name+": ] Basta! mi sono rotto, esco."); } try{ //sta un po' fuori... int timeToSleep = (int) (Math.random()*10); TimeUnit.SECONDS.sleep(timeToSleep); }catch(InterruptedException e){ e.printStackTrace(); } 275

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Es. Party
public class MainPR { //Simulazione del PR che invita la gente al party public static void main(String[] args) { //Definiamo il party di 20 persone concorrenti Party party = new Party(20); //creiamo 40 invitati Invited inLista[] = new Invited[40]; //met saranno lazy, gli altri no for(int i=0;i<inLista.length;i++){ Invited tmp; if (i%2==0) tmp = new Invited("NotLazy#"+i, party); else tmp = new LazyInvited("Lazy#"+i, party); } inLista[i]=tmp;

for(int i=0;i<inLista.length;i++){ inLista[i].start(); }

Leggere le specifiche per maggiori info


Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Nota: si possono anche creare semafori non onesti!


276

Come semplificarsi la vita


o In java esistono classi Thread-safe
o In passato solo Vector e HashTable o Oggi in java.util.concurrent
ConcurrentHashMap<K,V> ConcurrentLinkedQueue<E> ConcurrentSkipListMap<K,V> ConcurrentSkipListSet<E> CopyOnWriteArrayList<E> dell'implementazione thread safe da utilizzare per una HashMap una lista concatenata (sempre thread-safe) semplice implementazione thread-safe di una mappa navigabile (classe NavigableMap) implementazione thread-safe di una set navigabile (classe NavigableSet) quest'implementazione utile per liste accedute maggiormente con operazioni di lettura. In pratica la classe si trasforma in thread-safe al momento dell'accesso in scrittura (implementazione di un ArrayList sincronizzato) stessa cosa della classe precedente, solo che qui si tratta dell'implementazione di un ArraySet

CopyOnWriteArraySet<E>

Garantiscono laccesso sincronizzato alle risorse condivise senza che lutente si debba preoccupare della consistenza
Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

277

per la gestione di code


o o o
o Ovvero non sono sincronizzate

Le code in Java non implementano lattesa possibile sincronizzarle usando semafori o monitor oppure usando linterfaccia BlockingQueue e le sue implementazioni

o o o o o

ArrayBlockingQueue DelayQueue LinkedBlockingQueue PriorityBlockingQueue SynchronousQueue

o i metodi take() e put() (rimozione ed inserimento) sono bloccanti per il thread che li invoca
comportamento di coda con attesa ancora una volta non dovete preoccuparvi di nulla

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

278

per la gestione di code


public class MainTest { public static void main(String[] args) { //Creiamo un'istanza di coda "blocking" BlockingQueue queue = new LinkedBlockingQueue<String>(); //Processi consumer Consumer c1=new Consumer(queue); Consumer c2=new Consumer(queue); Consumer c3=new Consumer(queue); Consumer c4=new Consumer(queue); Consumer c5=new Consumer(queue); System.out.println("Starting Consumers...."); c1.start();c2.start();c3.start();c4.start();c5.start(); //Processo producer Producer p = new Producer(queue); System.out.println("Starting Consumers...."); p.start(); } }

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

279

per la gestione di code


class Consumer extends Thread{ //teniamo un counter a livello di classe static int id; int code; BlockingQueue<String> queue; public Consumer(BlockingQueue<String> queue) { this.queue = queue; code=++id; } public void run(){ //ciclo infinito while(true){ try { System.out.println("Thread#"+code+" waiting for the message..."); String message = queue.take(); System.out.println("Thread#"+code+" --> "+message+" taken!"); //riposa 2 secondi sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

280

per la gestione di code


class Producer extends Thread{ BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) { this.queue = queue; } public void run(){ int messagecode = 0; //ciclo infinito while(true){ try { System.out.println("Producing "+(++messagecode)); queue.put("MESSAGE@"+messagecode); System.out.println(messagecode+" in queue"); //riposa un secondo sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); con TimeUnit possiamo ragionare in termini di } } (SECONDS, MILLIS, MICROSECONDS, NANOSECONDS) } piuttosto che con i soli millisecondi
import static java.util.concurrent.TimeUnit.*; Thread.sleep(10000) // diventa Thread.sleep(TimeUnit.MILLISECONDS.convert(10, TimeUnit.SECONDS) // oppure Thread.sleep(TimeUnit.SECONDS.toMillis(10)) // ma anche TimeUnit.SECONDS.sleep(10) // infine 281 SECONDS.sleep(10);

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

10

o per gestire laccesso mutuo a singole variabili


import java.util.concurrent.atomic.AtomicInteger; class AtomicCounter { private AtomicInteger c = new AtomicInteger(0); public void increment() { c.incrementAndGet(); } public void decrement() { c.decrementAndGet(); } public int value() { return c.get(); } }

Permette alle implementazioni di Java di usare istruzioni atomiche efficienti a basso livello che sono disponibili sui processori moderni

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

282

o per gestire le eccezioni


o Cosa fare in caso di eccezioni a runtime?
o In passato
Il thread si interrompe passando la gestione dell'eccezione al ThreadGroup a cui appartiene Per poter gestire quest'eccezione dobbiamo creare una estensione di ThreadGroup e gestirne tutto il codice relativo

o E se non voglio implementare un ThreadGroup? o E se il thread non appartiene a nessun gruppo?


Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

283

11

o per gestire le eccezioni


o Exception Handler
o Si introduce un gestore di eccezioni
Thread.setDefaultUncaughtExceptionHandler() si aspetta come parametro un Thread.UncaughtExceptionHandler
che dobbiamo implementare

o quindi si definisce un comportamento di default per le eccezioni


allinterno del metodo Thread.UncaughtExceptionHandler.uncaughtException() che viene richiamato quando l'eccezione viene sollevata

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

284

o per gestire le eccezioni


public class ExceptionalThread extends Thread { private int iterations; public ExceptionalThread(String threadName,int iterations){ setName(threadName); this.iterations = iterations; setDefaultUncaughtExceptionHandler(new ThreadException()); } public void run(){ System.out.println("Running #"+getName()); if(iterations<0) throw new IllegalArgumentException("Numero di iterazioni negative!"); //altrimenti effettuiamo l'iterazione for (int i=0;i<iterations;i++){ System.out.println("Iterazione "+i+") "+getName()); } } } class ThreadException implements Thread.UncaughtExceptionHandler{ @Override public void uncaughtException(Thread arg0, Throwable arg1) { System.out.println(arg0+" ha sollevato la seguente eccezione: "+arg1.getMessage()); //qui potremmo gestire un flusso eccezionale, cosa che prima non poteva essere fatta } }
Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

285

12

E come complicarsela
o Problema
o la logica di business accoppiata con la logica di esecuzione (ciclo di vita) dello stesso Thread o Come disaccoppiare le due cose?

Soluzione: interfaccia java.util.concurrent.Executor (comoda per gli obiettivi pi comuni)


o definisce un flusso di esecuzione di thread java o nel contempo permette di controllarne la stessa esecuzione

i metodi statici di java.util.concurrent.Executors ci consentono l'accesso a degli Executor standard, che sono:
o o o o Single Thread Executor Fixed Thread Executor Cached Thread Executor Scheduled Thread Executor

Per obiettivi pi sofisticati c anche ExecutorService


o che ha un'interfaccia molto pi fornita
286

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

E come complicarsela
package it.html.threads; public class EchoServer { public static void main(String[] args) { ServerSocket server = null; //Avviamo il server try { server = new ServerSocket(6666); } catch (IOException e) { System.out.println("Eccezione all'avvio del server: "+e.getMessage()); System.exit(-1); } //Creo un pool di thread concorrenti ExecutorService executor = Executors.newFixedThreadPool(5); //Aspettiamo una nuova connessione cui deleghiamo l'esecuzione //all'executor service definito in precedenza while(true){ try{ System.out.println("Waiting incoming connection..."); Socket incoming = server.accept(); executor.execute(new EchoThread(incoming)); }catch(Exception e){ executor.shutdown(); } }

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

287

13

E come complicarsela
class EchoThread implements Runnable{ Socket socket; public EchoThread(Socket socket){ this.socket = socket; } public void run(){ System.out.println("Incoming connection: "+socket.getInetAddress()); //Continua con la lettura/scrittura sulla socket aperta... } }

Nota: Anzich effettuare l'esecuzione (con il classico metodo start()) qui deleghiamo l'esecuzione all'executor service, ignorando come questo gestisca la logica di avvio

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

288

Callable
o
o non pu essere modificato per poter essere gestito dallo scheduler della JVM o in soldoni, non avremo mai la possibilit di avere un thread che
ci restituisca un risultato con un'interrogazione diretta che sia in grado di sollevare checked exceptions

Altro problema: nella segnatura del metodo run() di un Thread

o ha un semplice metodo <T> call()

Soluzione: l'interfaccia Callable<T>


effettua un'attivit asincrona restituisce il risultato definito come parametro dell'interfaccia

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

289

14

Callable
immaginiamo di avere un task che restituisca il risultato di una ricerca su un catalogo di prodotti
public class CallableTask implements Callable<Collection<String>> { String keyword; public CallableTask(String key){ this.keyword = key; } @Override public Collection<String> call() throws EmptyListException{ Collection<String> toRet = new Vector<String>(); //... simuliamo la presenza di oggetti in un db toRet.add("Product #CADASD432"); toRet.add("Product #CADAS322"); //... se non ci sono oggetti solleviamo un'eccezione if (toRet.isEmpty()) throw new EmptyListException("Nessun elemento trovato!"); //... simuliamo il tempo di ricerca try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { // NOOP } } return toRet;

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

290

Callable
public static void main(String args[]) throws InterruptedException{ CallableTask task = new CallableTask("games"); System.out.println("Search result (passano 5 secondi prima di vedere il risultato):"); try { showResult(task.call()); } catch (EmptyListException e) { System.out.println("Nessun oggetto trovato"); } System.out.println("... fine della computazione"); } class EmptyListException extends Exception{ public EmptyListException(String string) { super(string); } }

o o

In teoria
o

In parole povere come un Runnable, ma in pi: 1. Ritorna un risultato 2. Solleva eccezioni

ci d la possibilit di avere un'esecuzione asincrona con un tipo di ritorno e una lista di eccezioni che desideriamo

In realt
o o

di asincrono c' ben poco l'esecuzione del metodo call() arresta l'esecuzione del flusso del main, in attesa del risultato Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011
S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

291

15

Future e FutureTask
o
o Future o FutureTask

Ci vengono in aiuto

o inseriamo l'esecuzione in un oggetto che


viene eseguito parallelamente restituisce il risultato quando ci serve
attendendo la fine del task se questo non completato

o Sottomettiamo a un ExecutorService un oggetto di tipo Callable<T> (tramite il metodo submit()) o Otteniamo come risultato un oggetto Future<T> e lo utilizziamo per
l'esecuzione concorrente di un'attivit recuperarne il risultato interrogando il metodo get (che restituisce un istanza di T) sul Future

Pi difficile a dirsi che a farsi

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

292

Future e FutureTask
Stessa operazione di ricerca ma in maniera parallela
public static void main(String args[]) throws InterruptedException{ //Definiamo un executor ExecutorService executor = Executors.newSingleThreadExecutor(); //Creiamo un task asincrono CallableTask task = new CallableTask("games"); //e lo wrappiamo in un oggetto future Future<Collection<String>> result = executor.submit(task); System.out.println("Search result (soliti 5 secondi):"); //...nel frattempo possiamo fare altri compiti, come preparare un layout di presentazione System.out.println("#------------- games found ---- ++"); //prendiamo il risultato, e se non stato eseguito attendiamo try { showResult(result.get()); } catch (ExecutionException e) { System.out.println("Eccezione nell'esecuzione della ricerca"); } System.out.println("... fine della computazione"); }

Nota: il metodo get(long,TimeUnit) permette di settare un timeout di attesa, oltre il quale l'attivit cancellata
utile ad esempio in contesti real-time o per attivit con deadline
Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

293

16

Fino alla Sincronizzazione avanzata: il locking


o Limitazioni di synchronized
o Multi-locking: quando abbiamo pi di una risorsa su cui effettuare il locking
Possono cio verificarsi degli stalli (deadlock)

o Timeout: attesa indefinita per una risorsa

quando una classe accede a un blocco synchronized non ha modo di uscirne finch quella sezione non liberata

o Interruptibility: come sopra

Non si pu interrompere e quindi saltare il blocco sincronizzato

Entrano in gioco i Lock


294

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Es. Lock rientrante


o o
o Regola il mutuo accesso ad una risorsa condivisa

Un lock simile a un synchronized

o Ma in questo caso pu essere rilasciato da thread diversi dal proprietario (I semafori non implementano nessuna funzionalit di appartenenza)

anche simile a un semaforo binario

o all'interno di esso possiamo definire altri Lock


Ma molto pi potente

cio possibile sviluppare logiche pi complesse esiste anche la classe Condition Se scade si eseguono altre azioni

o Oppure Lock con timeout (metodo tryLock())

Controparte delluso dei metodi Object monitor come wait e notify

o O ancora Lock che possono essere interrotti da thread diversi dallattuale proprietario

o Trovate tutte le informazioni nel package java.util.concurrent


Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

Al solito

295

17

Es. Lock rientrante


public class LockableAction { private Lock l; public LockableAction(){ //Creiamo un Reentrant lock, in modo //da non creare dei deadlock per cicli di lock interni l = new ReentrantLock(); } public void doSomethingConcurrent(){ //...svolge qualcosa non concorrente // acquisici il lock public void doSomethingConcurrentLimitedWait(){ l.lock(); //...svolge qualcosa non concorrente //...accedi alla //Prova ad acquisire il lock //...risorsa coperta try { //...dal lock //superati i 5 secondi salta l.unlock(); boolean acquired = l.tryLock(5,TimeUnit.SECONDS); } if (acquired){ //...azione 3 Lock rientrante //...azione 4 un processo pu //... } richiederlo (anche diverse } catch (InterruptedException e) { volte) senza bloccarsi sul e.printStackTrace(); lock stesso. }finally{ l.unlock(); Utile ad esempio in algoritmi di } visita di grafi } } Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 296 S. Bassis (bassis@dsi.unimi.it)
Universit di Milano D.S.I.

Java.util.concurrent
o Arricchisce Java di funzionalit utili alla gestione della concorrenza
o Oltre synchronized, wait, notify o Sia semplificando la vita o Sia complicandocela
pi flessibili pi strutturati

Con strutture dati sincronizzate

Ma introducendo Thread

Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

297

18

Per tirare le somme


Synchronized (wait + notify) Semafori Lock Atomic Variable Concurrent collection Executor Supportano meccanismi elementari di mutuo accesso Supportano meccanismi elementari di locking Supportano meccanismi sofisticati di locking Semplificano e rendono efficiente la sincronizzazione di tipi elementari Semplificano accesso e sincronizzazione di collezioni di dati Forniscono una gestione oculata di gruppi di Thread, - Callable: permettendo che questi ritornino un valore e gestendone le eccezioni - Future: anche in maniera asincrona

e siete fortunati, perch con Java 7 ne hanno aggiunti parecchi altri!!


Laboratorio di sistemi e reti (Comunicazione Digitale) - A.A. 2010-2011 S. Bassis (bassis@dsi.unimi.it) Universit di Milano D.S.I.

298

19

Potrebbero piacerti anche