11.04.2013 Views

Progetto di reti di Calcolatori e Sistemi Informativi

Progetto di reti di Calcolatori e Sistemi Informativi

Progetto di reti di Calcolatori e Sistemi Informativi

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

1<br />

<strong>Progetto</strong> <strong>di</strong> <strong>reti</strong> <strong>di</strong> <strong>Calcolatori</strong> e <strong>Sistemi</strong><br />

Informatici<br />

PdR_09010 - Stefano Millozzi<br />

Stefano Millozzi


2<br />

java.util.concurrent<br />

PdR_09010 - Stefano Millozzi<br />

Introduzione al package


Le Motivazioni <strong>di</strong>etro java.util.concurrent<br />

Nuovo package ideato da un gruppo <strong>di</strong> esperti sotto la<br />

guida <strong>di</strong> Doug Lea (JSR166)‏<br />

Nasce dall’osservazione, che java, nonostante prevedesse<br />

dei meccanismi per la programmazione concorrente sin<br />

dalla nascita, da allora non ha fatto significativi passi in<br />

avanti in questo campo<br />

I meccanismi che mette a <strong>di</strong>sposizione sono piuttosto<br />

primitivi e rigi<strong>di</strong>; necessità <strong>di</strong> astrazione<br />

Obbiettivi, <strong>di</strong>chiaratamente immodesti, creare una<br />

libreria che rappresenti per la programmazione<br />

concorrente, quello che il Java Collections Framework ha<br />

rappresentato per le collezioni<br />

3<br />

PdR_09010 - Stefano Millozzi


J2SE 5.0 e la Programmazione Concorrente<br />

Package <strong>di</strong> principale interesse<br />

java.util.concurrent<br />

framework per la scrittura <strong>di</strong> co<strong>di</strong>ce concorrente<br />

java.util.concurrent.locks<br />

framework con gli usuali strumenti a supporto dei problemi <strong>di</strong><br />

competizione e cooperazione<br />

java.util.concurrent.atomic<br />

classi wrapper che offrono operazioni atomiche grazie ad istruzioni<br />

del tipo TestAndSet, qui chiamate CompareAndSet (CAS), aggiunte al<br />

supporto della JVM<br />

alcune novità hanno causato cambiamenti anche in altri<br />

package<br />

ad es. java.util.Queue<br />

4<br />

PdR_09010 - Stefano Millozzi


5<br />

Lightweight Executable Framework<br />

Thread all’ennesima potenza<br />

PdR_09010 - Stefano Millozzi


Lightweight Executable Framework<br />

Con i meccanismi nativi <strong>di</strong> java dobbiamo contestualmente<br />

creare i thread<br />

new Thread()‏<br />

invocazione del metodo Thread.start()‏<br />

sottomettere i task da eseguire<br />

sarà quello associabile al metodo run()<br />

Vogliamo <strong>di</strong>saccoppiare i due aspetti, per poter variare uno senza dover cambiare<br />

anche l’altro<br />

Idea <strong>di</strong> base: lo stesso thread può far avanzare molteplici attività non correlate<br />

(worker thread)‏<br />

Il <strong>di</strong>saccoppiamento tra la sottomissione <strong>di</strong> Runnable (in generale task) ed il modo<br />

in cui saranno effettivamente eseguiti consente <strong>di</strong> variare in<strong>di</strong>pendentemente la<br />

politica <strong>di</strong> esecuzione<br />

Ad es. si possono applicare politiche tese a:<br />

contenere il numero <strong>di</strong> thread creati<br />

pooling dei thread, caching e riuso dei thread<br />

attuare una politica <strong>di</strong> priorità personalizzata<br />

programmare temporalmente l’esecuzione<br />

6<br />

PdR_09010 - Stefano Millozzi


Le classi in gioco/1: Executor<br />

Executor<br />

An object that executes submitted Runnable tasks.<br />

ExecutorService (extends Executor)<br />

An Executor that provides methods to manage termination and<br />

methods that can produce a Future for tracking progress of one or<br />

more asynchronous tasks.<br />

ScheduledExecutorService (extends ExecutorService)<br />

An ExecutorService that can schedule commands to run after a given<br />

delay, or to execute perio<strong>di</strong>cally.<br />

Executors<br />

7<br />

Factory and utility methods for Executor, ExecutorService,<br />

ScheduledExecutorService, ThreadFactory, and Callable classes<br />

defined in this package.<br />

PdR_09010 - Stefano Millozzi


Le classi in gioco/2: Task & sync<br />

Runnable<br />

The Runnable interface should be implemented by any class whose<br />

instances are intended to be executed by a thread. The class must<br />

define a method of no arguments called run.<br />

Callable<br />

A task that returns a result and may throw an exception.<br />

Implementors define a single method with no arguments called call.<br />

Future<br />

8<br />

A Future represents the result of an asynchronous computation.<br />

Methods are provided to check if the computation is complete, to<br />

wait for its completion, and to retrieve the result of the computation.<br />

The result can only be retrieved using method get when the<br />

computation has completed, blocking if necessary until it is ready.<br />

Cancellation is performed by the cancel method.<br />

PdR_09010 - Stefano Millozzi


Executor<br />

Executor<br />

9<br />

Motore in grado <strong>di</strong> mandare in esecuzione un oggetto Runnable<br />

L’interfaccia alla base <strong>di</strong> questo framework è<br />

Method java.util.concurrent.Executor<br />

Summary<br />

void<br />

execute(Runnable command)<br />

Executes the given command at some time in<br />

the future.<br />

Sono fornite possibili implementazioni dai meto<strong>di</strong> factory della<br />

classe <strong>di</strong> utilità java.util.concurrent.Executors<br />

ExecutorService<br />

ScheduledExecutorService<br />

PdR_09010 - Stefano Millozzi


Executors<br />

Method Summary<br />

java.util.concurrent.Executors<br />

Factory per ottenere oggetti after a given Executor delay, or to per execute poter perio<strong>di</strong>cally. sottomettere nuovi<br />

task<br />

10<br />

static<br />

ExecutorService<br />

static<br />

ExecutorService<br />

static<br />

ScheduledExecutorService<br />

static<br />

ExecutorService<br />

static<br />

ScheduledExecutorService<br />

newCachedThreadPool() Creates a thread pool that creates new<br />

threads as needed, but will reuse previously constructed threads<br />

when available.<br />

newFixedThreadPool(int nThreads)<br />

Creates a thread pool that reuses a fixed set of threads operating off<br />

a shared unbounded queue.<br />

newScheduledThreadPool(int corePoolSize)<br />

Creates a thread pool that can schedule cmds to run after a given<br />

delay, or to execute perio<strong>di</strong>cally.<br />

newSingleThreadExecutor()<br />

Creates an Executor that uses a single worker thread operating off an<br />

unbounded queue.<br />

newSingleThreadScheduledExecutor()<br />

Creates a single-threaded executor that can schedule cmds to run<br />

PdR_09010 - Stefano Millozzi


Esempio<br />

Realizzare un programma semplice programma Java che<br />

avvia 5 task in parallelo, usando il metodo<br />

newCachedThreadPool() <strong>di</strong> Executors<br />

11<br />

Ogni task stampa un messaggio <strong>di</strong> avvio, poi si mette in sleep<br />

per 5 secon<strong>di</strong> e uno <strong>di</strong> termine.<br />

Ripetere lo stesso test usando il metodo<br />

newFixedThreadPool(3) <strong>di</strong> Executors<br />

PdR_09010 - Stefano Millozzi


ExecutorService/1<br />

Method Summary<br />

Future<br />

Future<br />

Future<br />

Meto<strong>di</strong> per la sottomissione dei lavori<br />

12<br />

java.util.concurrent<br />

Interface ExecutorService<br />

All Superinterfaces:<br />

Executor<br />

All Known Subinterfaces:<br />

ScheduledExecutorService<br />

submit(Callable task) Submits a value-returning task for<br />

execution and returns a Future representing the pen<strong>di</strong>ng results of<br />

the task.<br />

submit(Runnable task) Submits a Runnable task for execution and<br />

returns a Future representing that task.<br />

submit(Runnable task, T result) Submits a Runnable task for<br />

execution and returns a Future representing that task that will upon<br />

completion return the given result<br />

PdR_09010 - Stefano Millozzi


ExecutorService /2<br />

meto<strong>di</strong> per la gestione della fine servizio <strong>di</strong> esecuzione<br />

Method Summary<br />

13<br />

boolean<br />

boolean<br />

boolean<br />

void<br />

List<br />

awaitTermination(long timeout, TimeUnit unit) Blocks until all tasks have<br />

completed execution …<br />

isShutdown() Returns true if this executor has been shut down.<br />

isTerminated() Returns true if all tasks have completed…<br />

shutdown() Initiates an orderly shutdown in which previously submitted<br />

tasks are executed, but no new tasks will be accepted.<br />

shutdownNow() Attempts to stop all actively executing tasks, halts the<br />

processing of waiting tasks, and returns a list of the tasks that were<br />

awaiting execution.<br />

PdR_09010 - Stefano Millozzi


Note sull’uso del metodo Shutdown<br />

Quando si crea un oggetto ExecutorService, vengono avviati<br />

anche alcuni thread <strong>di</strong> servizio interni ad essa<br />

Questi thread vendono mantenuti attivi per tutto il tempo in cui è<br />

possibile sottomettere nuovi task<br />

vengono terminati solo quando si esegue shutdown e terminano i<br />

Task da elaborare<br />

Se non si esegue il comando shutdown/shutdowNow, la JVM<br />

resta attiva anche se il metodo main è terminato.<br />

Restano infatti attivi i thread interni degli oggetti ExecutorService<br />

Esempio<br />

@see TestExecutors1.java<br />

Commentare le istruzioni e.shutdown() nei meto<strong>di</strong> test1 e test2 e<br />

verificare se il processo JVM alla fine del programma termina oppure no<br />

14<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.Callable<br />

Method Summary<br />

V<br />

Introdotti per gli ExecutorService<br />

Simile a java.lang.Runnable ma consente <strong>di</strong> restituire un valore<br />

15<br />

call()<br />

Computes a result, or throws an exception if unable to do so.<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.Future (1)‏<br />

Con i lightweight executable framework, la sottomissione<br />

<strong>di</strong> un task viene <strong>di</strong>saccoppiata dall’effettiva esecuzione<br />

I task saranno eseguiti “at some time in the future”<br />

Pertanto, in seguito alla sottomissione gli Executor non<br />

restituiscono i risultati della computazione, ma Future,<br />

ovvero oggetti che incapsulano un risultato<br />

potenzialmente ancora da calcolare<br />

Se si cerca <strong>di</strong> leggere un risultato non ancora <strong>di</strong>sponibile<br />

si deve aspettare<br />

16<br />

PdR_09010 - Stefano Millozzi


Future (2)‏<br />

Method Summary<br />

boolea<br />

n<br />

boolea<br />

n<br />

17<br />

V<br />

V<br />

boolea<br />

n<br />

cancel(boolean mayInterruptIfRunning) Attempts to cancel<br />

execution of this task.<br />

get() Waits if necessary for the computation to complete, and then<br />

retrieves its result.<br />

get(long timeout, TimeUnit unit) Waits if necessary for at most<br />

the given time for the computation to complete, and then retrieves<br />

its result, if available.<br />

isCancelled() Returns true if this task was cancelled before it<br />

completed normally.<br />

isDone() Returns true if this task completed.<br />

PdR_09010 - Stefano Millozzi


Un Esempio <strong>di</strong> Utilizzo <strong>di</strong> Callable<br />

Scrivere un algoritmo <strong>di</strong> or<strong>di</strong>namento parallelo in cui il<br />

vettore iniziale viene <strong>di</strong>viso in varie porzioni, or<strong>di</strong>nate<br />

separatamente e poi fuse insieme (merge or<strong>di</strong>nato)<br />

18<br />

Confrontare i tempi <strong>di</strong> esecuzione tra l’approccio parallelo e<br />

uno sequenziale in cui il vettore non viene separato<br />

Confrontare anche la soluzione in cui l’or<strong>di</strong>namento dei due<br />

subarray è eseguito sequenzialmente<br />

Utilizzare un algoritmo <strong>di</strong> or<strong>di</strong>namento inefficiente O(n^2) per<br />

poter meglio apprezzare l’effetto della ripartizione del lavoro<br />

@see ParallelSorting.java<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.FutureTask<br />

Constructor Summary<br />

FutureTask(Callable callable)<br />

Creates a FutureTask that will upon running, execute the given Callable.<br />

FutureTask(Runnable runnable, V result)<br />

Creates a FutureTask that will upon running, execute the given Runnable,<br />

and arrange that get will return the given result on successful completion.<br />

19<br />

PdR_09010 - Stefano Millozzi


ScheduledExecutorService<br />

Method Summary<br />

ScheduledFuture<br />

ScheduledFuture<br />

ScheduledFuture<br />

ScheduledFuture<br />

Permette <strong>di</strong> schedulare i task ad intervalli regolari<br />

Utilizzo come timer<br />

20<br />

schedule(Callable callable, long delay, TimeUnit unit)<br />

Creates and executes a ScheduledFuture that becomes enabled<br />

after the given delay.<br />

schedule(Runnable command, long delay, TimeUnit unit)<br />

Creates and executes a one-shot action that becomes enabled<br />

after the given delay.<br />

scheduleAtFixedRate(Runnable command, long initialDelay,<br />

long period, TimeUnit unit)<br />

Creates and executes a perio<strong>di</strong>c action ….<br />

scheduleWithFixedDelay(Runnable command,<br />

long initialDelay, long delay, TimeUnit unit)<br />

Creates and executes a perio<strong>di</strong>c action …<br />

PdR_09010 - Stefano Millozzi


Conseguenze nell’uso <strong>di</strong> Executable Framework (1)‏<br />

Per<strong>di</strong>ta <strong>di</strong> identità dei thread – ovvero della<br />

corrispondenza thread / task<br />

i thread possono essere riciclati e comunque si perde l’identità<br />

dell’oggetto java.lang.Thread<br />

Le <strong>di</strong>pendenze reciproche tra thread sono pericolose<br />

perché la politica <strong>di</strong> esecuzione potrebbe causare<br />

l’invalidamento dell’assunzione <strong>di</strong> progresso finito.<br />

Possibili soluzioni:<br />

prevedere un adeguato numero <strong>di</strong> worker thread<br />

usarli solo nel caso che è noto non possano esserci<br />

<strong>di</strong>pendenze, ad es. sessioni http<br />

creare politiche <strong>di</strong> gestione delle code che tengano conto delle<br />

<strong>di</strong>pendenze<br />

21<br />

PdR_09010 - Stefano Millozzi


Conseguenze dei Lightweight Executable Framework<br />

(2)‏<br />

Saturazione del pool <strong>di</strong> worker thread a causa<br />

dell’eccessiva frequenza delle richieste. Soluzioni:<br />

22<br />

aumentare il numero <strong>di</strong> worker thread<br />

usare un buffer illimitato <strong>di</strong> accodamento delle richieste<br />

back-pressure: far <strong>di</strong>minuire la frequenza delle richieste<br />

con protocolli più articolati che prevedono interazioni con il client<br />

ritardando l’accettazione <strong>di</strong> nuove richieste<br />

passando in modalità <strong>di</strong> servizio single-threaded<br />

fare il lavoro nel thread <strong>di</strong> chi sottomette i task per rallentarlo<br />

abbandonare alcune richieste<br />

le richieste nuove (barbiere dormiente)‏<br />

le richieste vecchie<br />

PdR_09010 - Stefano Millozzi


Conseguenze dei Lightweight Executable Framework<br />

(3)‏<br />

Gestione dei thread<br />

23<br />

lazy construction: i worker thread sono creati ed organizzati in<br />

un pool la cui <strong>di</strong>mensione può rispondere a queste esigenze<br />

contrastanti<br />

minimizzare il tempo <strong>di</strong> creazione dei thread<br />

riciclare vecchi thread per servire nuove richieste<br />

ottimizzare lo sfruttamento delle risorse allocate<br />

eliminare thread allo scadere <strong>di</strong> un time-out sull’inattività<br />

PdR_09010 - Stefano Millozzi


Conseguenze dei Lightweight Executable Framework<br />

(4)‏<br />

24<br />

Cancellazione dei thread. Bisogna <strong>di</strong>stinguere tra cancellazione<br />

<strong>di</strong> un task<br />

<strong>di</strong> un worker thread<br />

<strong>di</strong> un ExecutorService<br />

PdR_09010 - Stefano Millozzi


Riepilogo delle principali classi<br />

25<br />

Executor<br />

ExecutorService<br />

Executors<br />

(factory)<br />

ScheduledExecutorService<br />

execute()<br />

schedule()<br />

submit()<br />

Future<br />

Scheduled<br />

Future<br />

PdR_09010 - Stefano Millozzi<br />

Callable<br />

Runnable


26<br />

Lock e Semafori<br />

Primitive <strong>di</strong> sincronizzazione<br />

PdR_09010 - Stefano Millozzi


Lock e Con<strong>di</strong>tion<br />

Costrutto che può essere introdotto al posto dei meto<strong>di</strong><br />

synchronized, wait e signal<br />

27<br />

Lock permette <strong>di</strong> regolare l’accesso (mutua esclusione) <strong>di</strong> una<br />

parte <strong>di</strong> co<strong>di</strong>ce, in maniera simile a quanto accade per<br />

synchonized(oggetto){}<br />

Con la classe Con<strong>di</strong>tion (ricavata <strong>di</strong>rettamente dal Lock) è<br />

possibile fare wait e signal<br />

PdR_09010 - Stefano Millozzi


I Lock: java.util.concurrent.locks.Lock<br />

Method Summary<br />

Lock implementations provide more extensive locking operations than can<br />

be obtained using synchronized methods and statements.<br />

28<br />

void<br />

void<br />

Con<strong>di</strong>tion<br />

boolean<br />

boolean<br />

void<br />

lock() Acquires the lock.<br />

lockInterruptibly() Acquires the lock unless the current thread is<br />

interrupted.<br />

newCon<strong>di</strong>tion() Returns a new Con<strong>di</strong>tion instance that is bound to<br />

this Lock instance.<br />

tryLock() Acquires the lock only if it is free at the time of invocation.<br />

tryLock(long time, TimeUnit unit) Acquires the lock if it is free<br />

within the given waiting time and the current thread has not been<br />

interrupted.<br />

unlock() Releases the lock.<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.locks.Con<strong>di</strong>tion<br />

Method Summary<br />

void await() Causes the current thread to wait until it is signalled or interrupted.<br />

boolean<br />

Con<strong>di</strong>tion factors out the Object monitor methods (wait, notify and<br />

notifyAll) into <strong>di</strong>stinct objects to give the effect of having multiple<br />

wait-sets per object, by combining them with the use of arbitrary<br />

Lock implementations.<br />

29<br />

long<br />

void<br />

boolean<br />

void<br />

void<br />

await(long time, TimeUnit unit) Causes the current thread to wait until it is signalled or<br />

interrupted, or the specified waiting time elapses.<br />

awaitNanos(long nanosTimeout) …<br />

awaitUninterruptibly() Causes the current thread to wait until it is signalled.<br />

awaitUntil(Date deadline) ….<br />

signal() Wakes up one waiting thread.<br />

signalAll() Wakes up all waiting threads.<br />

PdR_09010 - Stefano Millozzi


Implementazioni <strong>di</strong> Lock e Con<strong>di</strong>tion<br />

java.util.concurrent.locks.Lock e<br />

java.util.concurrent.locks.Con<strong>di</strong>tion sono interfacce<br />

java.util.concurrent.locks.ReentrantLock è una<br />

implementazione <strong>di</strong> lock rientranti<br />

30<br />

lo stesso thread può fare più lock() senza bloccarsi<br />

con il metodo factory Lock.newCon<strong>di</strong>tion() si ottiene una<br />

implementazione anonima delle variabili con<strong>di</strong>zione<br />

PdR_09010 - Stefano Millozzi


Esempio <strong>di</strong> Utilizzo dei Lock<br />

Lock l = una implementazione <strong>di</strong> Lock;<br />

l.lock();<br />

try {<br />

}<br />

31<br />

regione critica sulla risorsa con<strong>di</strong>visa<br />

eventuale wait, se la risorsa non è <strong>di</strong>sponibile<br />

finally {<br />

}<br />

l.unlock();<br />

PdR_09010 - Stefano Millozzi


Buffer Circolare (1)‏<br />

Implementiamo un buffer circolare <strong>di</strong> <strong>di</strong>mensione finita<br />

adatto ad essere utilizzato da molteplici thread<br />

N.B. la classe ArrayBlockingQueue fornisce le medesime<br />

funzionalità<br />

Utilizziamo<br />

32<br />

Lock<br />

Variabili Con<strong>di</strong>zione<br />

PdR_09010 - Stefano Millozzi


Buffer Circolare (2)‏<br />

import java.util.concurrent.*;<br />

public class BoundedBuffer {<br />

33<br />

private Data buffer[];<br />

private int first;<br />

private int last;<br />

private int numberInBuffer;<br />

private int size;<br />

private Lock lock = new ReentrantLock();<br />

private final Con<strong>di</strong>tion notFull = lock.newCon<strong>di</strong>tion();<br />

private final Con<strong>di</strong>tion notEmpty = Lock.newCon<strong>di</strong>tion();<br />

//…<br />

PdR_09010 - Stefano Millozzi


Buffer Circolare (3)‏<br />

//…<br />

public BoundedBuffer(int length) {<br />

}<br />

34<br />

size = length;<br />

buffer = (Data[]) new Object[size];<br />

last = 0;<br />

first = 0;<br />

numberInBuffer = 0;<br />

//…<br />

PdR_09010 - Stefano Millozzi


Buffer Circolare (4)‏<br />

public void put(Data item)‏throws InterruptedException {<br />

lock.lock();<br />

try {<br />

while (numberInBuffer == size)<br />

notFull.await();<br />

last = (last + 1) % size;<br />

numberInBuffer++;<br />

buffer[last] = item;<br />

notEmpty.signal();<br />

} finally {<br />

lock.unlock();<br />

}<br />

}<br />

//…<br />

35<br />

PdR_09010 - Stefano Millozzi


Buffer Circolare (5)‏<br />

//…<br />

public Data get() throws InterruptedException {<br />

lock.lock();<br />

try {<br />

while (numberInBuffer == 0)<br />

notEmpty.await();<br />

first = (first + 1) % size ;<br />

numberInBuffer--;<br />

notFull.signal();<br />

return buffer[first];<br />

} finally {<br />

lock.unlock();<br />

}<br />

}<br />

}<br />

36<br />

PdR_09010 - Stefano Millozzi


Read/Write Lock (1)‏<br />

Si tratta <strong>di</strong> una coppia <strong>di</strong> lock associati tra loro<br />

37<br />

uno, detto lock <strong>di</strong> lettura (read lock), consente ai thread che lo<br />

possiedono <strong>di</strong> fare letture sulla risorsa protetta<br />

l’altro, detto lock <strong>di</strong> scrittura (write lock), consente ai thread<br />

che lo possiedono <strong>di</strong> fare scritture sulla risorsa protetta<br />

Utili per proteggere l’accesso a strutture con<strong>di</strong>vise<br />

evitando fenomeni <strong>di</strong> interferenza e lunghe attese<br />

(passive) senza compromettere eccessivamente il grado <strong>di</strong><br />

parallelismo in presenza <strong>di</strong><br />

una maggioranza per numero e durata <strong>di</strong> operazioni <strong>di</strong> lettura<br />

una minoranza per numero e durata <strong>di</strong> operazioni <strong>di</strong> scrittura<br />

PdR_09010 - Stefano Millozzi


Read/Write Lock (2)‏<br />

Molteplici thread possono ottenere il read lock<br />

concorrentemente<br />

Solo un thread alla volta può ottenere il write lock<br />

Se un thread chiede il lock <strong>di</strong> lettura, può procedere<br />

anche se altri thread già lo possiedono ma solo se<br />

nessuno già possiede il lock in scrittura<br />

Se un thread chiede il lock in scrittura, può procedere<br />

solo quando ottiene l’accesso in mutua esclusione alla<br />

risorsa<br />

38<br />

tutti gli altri thread non possono <strong>di</strong>sporre né del lock <strong>di</strong><br />

scrittura né <strong>di</strong> quello in lettura<br />

PdR_09010 - Stefano Millozzi


Read/Write Lock (3)‏<br />

In un certo senso, i read/write lock rispecchiano il<br />

concetto che le aree <strong>di</strong> memoria hanno molteplicità<br />

39<br />

infinita in lettura<br />

seriale in scrittura<br />

Rispetto alla semplice mutua esclusione, il livello <strong>di</strong><br />

parallelismo raggiungibile <strong>di</strong>pende fortemente dalla<br />

frequenza e dal tipo <strong>di</strong> accessi lettura/scrittura<br />

PdR_09010 - Stefano Millozzi


Read/Write Lock (4)‏<br />

Alcune delle scelte progettuali necessarie:<br />

40<br />

se sia scrittori che lettori sono in attesa passiva, chi preferire<br />

non appena la risorsa si libera da uno scrittore? e da un<br />

lettore?<br />

il lock <strong>di</strong> scrittura è rientrante? e quello <strong>di</strong> lettura?<br />

un lock <strong>di</strong> lettura può essere promosso a lock <strong>di</strong> scrittura?<br />

secondo quali politiche?<br />

un lock <strong>di</strong> scrittura può essere retrocesso a lock <strong>di</strong> lettura?<br />

secondo quali politiche?<br />

…<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.locks.ReadWriteLock<br />

Method Summary<br />

Lock<br />

Lock<br />

41<br />

readLock() Returns the lock used for rea<strong>di</strong>ng.<br />

writeLock() Returns the lock used for writing.<br />

PdR_09010 - Stefano Millozzi


ReentrantReadWriteLock (1)‏<br />

Constructor Summary<br />

ReentrantReadWriteLock()<br />

Creates a new ReentrantReadWriteLock with default ordering properties.<br />

ReentrantReadWriteLock(boolean fair)<br />

Creates a new ReentrantReadWriteLock with the given fairness policy.<br />

Una possibile implementazione<br />

una politica fair (opzionale) <strong>di</strong> gestione delle richieste<br />

lock rientranti<br />

locking interrompibile sia sul lock <strong>di</strong> lettura che <strong>di</strong> scrittura<br />

lock in scrittura possono essere declassati a lock in lettura<br />

i lock in scrittura supportano le variabili con<strong>di</strong>zione<br />

42<br />

PdR_09010 - Stefano Millozzi


ReentrantReadWriteLock (2)‏<br />

43<br />

PdR_09010 - Stefano Millozzi


Esempio: ReadWriteLock (1)‏<br />

class RWDictionary {<br />

private final Map m = new TreeMap();<br />

private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();<br />

private final Lock r = rwl.readLock();<br />

private final Lock w = rwl.writeLock();<br />

public Data get(String key) {<br />

r.lock(); //lock in lettura per leggere (anche + thread in parallelo)<br />

try { return m.get(key); }<br />

finally { r.unlock(); }<br />

}<br />

public String[] allKeys() {<br />

r.lock();<br />

try { return m.keySet().toArray(); }<br />

finally { r.unlock(); }<br />

}<br />

… // altri meto<strong>di</strong> a seguire<br />

}<br />

44<br />

PdR_09010 - Stefano Millozzi


Esempio: ReadWriteLock (2)‏<br />

class RWDictionary {<br />

…<br />

public Data put(String key, Data value) {<br />

w.lock(); //una sola scrittura ammessa, non mentre ci sono letture<br />

try { return m.put(key, value); }<br />

finally { w.unlock(); }<br />

}<br />

public void clear() {<br />

w.lock();<br />

try { m.clear(); }<br />

finally { w.unlock(); }<br />

}<br />

}<br />

45<br />

PdR_09010 - Stefano Millozzi


Altri possibili utilizzi<br />

Accesso controllato ad un file<br />

46<br />

Più thread possono accedere in lettura in parallelo (usando<br />

Stream <strong>di</strong>fferenti) ma solo uno in scrittura (per esempio in<br />

append)<br />

Utilizzo <strong>di</strong> aree <strong>di</strong> memoria con<strong>di</strong>vise<br />

…<br />

PdR_09010 - Stefano Millozzi


I Semafori: java.util.concurrent.Semaphore<br />

Constructor Summary<br />

Semaphore(int permits) Creates a Semaphore with the given number of permits and<br />

nonfair fairness setting.<br />

Method Summary<br />

void<br />

void<br />

boolean<br />

Utilizzati per il controllo <strong>di</strong> accesso alle risorse<br />

Utilizzi classici per delimitare co<strong>di</strong>ce ad accesso controllato<br />

47<br />

int<br />

acquire(int permits) Acquires the given number of permits from this semaphore, blocking<br />

until all are available, or the thread is interrupted.<br />

availablePermits() Returns the current number of permits available<br />

release(int permits) Releases the given number of permits, …<br />

tryAcquire(int permits) Acquires the given number of permits from this<br />

semaphore, only if all are available at the time of invocation.<br />

PdR_09010 - Stefano Millozzi


48<br />

PdR_09010 - Stefano Millozzi<br />

Barrier e Latch


java.util.concurrent.CountDownLatches (1)‏<br />

A synchronization aid that allows one or more threads to wait until a set of<br />

operations being performed in other threads completes.<br />

Constructor Summary<br />

CountDownLatch(int count)<br />

Constructs a CountDownLatch initialized with the given count.<br />

Method Summary<br />

49<br />

void<br />

boolean<br />

void<br />

long<br />

String<br />

await() Causes the current thread to wait until the latch has counted down to zero, unless<br />

the thread is interrupted.<br />

await(long timeout, TimeUnit unit) Causes the current thread to wait until the latch has<br />

counted down to zero, unless the thread is interrupted, or the specified waiting time<br />

elapses.<br />

countDown() Decrements the count of the latch, releasing all waiting threads if the count<br />

reaches zero.<br />

getCount() Returns the current count.<br />

toString() Returns a string identifying this latch, as well as its state.<br />

PdR_09010 - Stefano Millozzi


CountDownLatches (2)‏<br />

class Driver { // ...<br />

void main() throws InterruptedException {<br />

CountDownLatch startSignal = new CountDownLatch(1);<br />

CountDownLatch doneSignal = new CountDownLatch(N);<br />

for (int i = 0; i < N; ++i)//create/start threads<br />

new Thread(<br />

new Worker(startSignal,doneSignal)).start();<br />

doSomethingElse(); // don't let run yet<br />

startSignal.countDown();// let all threads proceed<br />

doSomethingElse();<br />

doneSignal.await(); // wait for all to finish<br />

}<br />

<br />

50<br />

C’è un Driver e <strong>di</strong>versi thread Worker<br />

startSignal serve ad impe<strong>di</strong>re che i Worker procedano prima che il<br />

Driver è pronto<br />

doneSignal serve al Driver per attendere che tutti i Worker<br />

abbiano terminato<br />

PdR_09010 - Stefano Millozzi


CountDownLatches (3)‏<br />

class Worker implements Runnable {<br />

private final CountDownLatch startSignal;<br />

private final CountDownLatch doneSignal;<br />

Worker(CountDownLatch startSignal,<br />

CountDownLatch doneSignal) {<br />

this.startSignal = startSignal;<br />

this.doneSignal = doneSignal;<br />

}<br />

public void run() {<br />

try {<br />

startSignal.await();<br />

doWork();<br />

doneSignal.countDown();<br />

} catch (InterruptedException ex) {}// return;<br />

}<br />

void doWork() { ... }<br />

}<br />

51<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.CyclicBarrier (1)‏<br />

Consente <strong>di</strong> fermare e sincronizzare dei thread presso una<br />

barriera in attesa che tutti i partecipanti la raggiungono<br />

Utile per la decomposizione <strong>di</strong> problemi in sottoproblemi<br />

da risolvere concorrentemente<br />

Constructor Summary<br />

CyclicBarrier(int parties) Creates a new CyclicBarrier that will trip when the given<br />

number of parties (threads) are waiting upon it, and does not perform a predefined<br />

action upon each barrier.<br />

CyclicBarrier(int parties, Runnable barrierAction) Creates a new CyclicBarrier<br />

that will trip when the given number of parties (threads) are waiting upon it, and<br />

which will execute the given barrier action when the barrier is tripped, performed by<br />

the last thread entering the barrier.<br />

52<br />

PdR_09010 - Stefano Millozzi


CyclicBarrier (2)‏<br />

I sottoproblemi si affidano a thread <strong>di</strong>stinti<br />

La barriera serve per sincronizzarsi sulla loro fine e ricomporne i risultati<br />

Method Summary<br />

int<br />

int<br />

int<br />

int<br />

boolean<br />

void 53<br />

await() Waits until all parties have invoked await on this barrier.<br />

await(long timeout, TimeUnit unit) Waits until all parties have invoked<br />

await on this barrier.<br />

getNumberWaiting() the n. of parties currently waiting at the barrier.<br />

getParties() Returns the n. of parties required to trip this barrier.<br />

isBroken() Queries if this barrier is in a broken state.<br />

reset() Resets the barrier to its initial state.<br />

PdR_09010 - Stefano Millozzi


CyclicBarrier (3)‏<br />

class Solver {<br />

final int N;<br />

final float[][] data;<br />

final CyclicBarrier barrier;<br />

<br />

class Worker implements Runnable {<br />

int myRow;<br />

Worker(int row) { myRow = row; }<br />

public void run() {<br />

while (!done()) {<br />

processRow(myRow);<br />

try { barrier.await(); }<br />

catch (InterruptedException ex){ return; }<br />

catch (BrokenBarrierException ex){ return;}<br />

}<br />

} //…<br />

54<br />

PdR_09010 - Stefano Millozzi


CyclicBarrier (4)‏<br />

//…<br />

public Solver(float[][] matrix) {<br />

data = matrix;<br />

N = matrix.length;<br />

barrier =<br />

new CyclicBarrier(N,<br />

new Runnable() {<br />

public void run() {<br />

mergeRows(...);<br />

}<br />

});<br />

for (int i = 0; i < N; ++i)<br />

new Thread(new Worker(i)).start();<br />

waitUntilDone();<br />

}<br />

}<br />

55<br />

PdR_09010 - Stefano Millozzi


java.util.concurrent.CompletionService (1)‏<br />

Interfaccia che permette <strong>di</strong> <strong>di</strong>saccoppiare la<br />

sottomissione <strong>di</strong> nuovi task da eseguire, dalla<br />

consumazione dei corrispondenti risultati<br />

I risultati sono consumati nell’or<strong>di</strong>ne temporale in cui i<br />

task sono completati, in<strong>di</strong>pendentemente dall’or<strong>di</strong>ne <strong>di</strong><br />

sottomissione<br />

56<br />

PdR_09010 - Stefano Millozzi


CompletionService (2)‏<br />

Method Summary<br />

Future<br />

Future<br />

Future<br />

Future<br />

Future<br />

57<br />

poll() Retrieves and removes the Future representing the next<br />

completed task or null if none are present.<br />

poll(long timeout, TimeUnit unit) Retrieves and removes the Future<br />

representing the next completed task, waiting if necessary up to the<br />

specified wait time if none are yet present.<br />

submit(Callable task) Submits a value-returning task for<br />

execution and returns a Future representing the pen<strong>di</strong>ng results of the<br />

task.<br />

submit(Runnable task, V result) Submits a Runnable task for<br />

execution and returns a Future representing that task.Upon completion,<br />

this task may be taken or polled.<br />

take() Retrieves and removes the Future representing the next<br />

completed task, waiting if PdR_09010 none are - Stefano yet present. Millozzi


java.util.concurrent.ExecutorCompletionService<br />

Implementazione del CompetitionSercive, agganciato ad<br />

un Executor<br />

Constructor Summary<br />

ExecutorCompletionService(Executor executor) Creates an<br />

ExecutorCompletionService using the supplied executor for base task execution<br />

and a LinkedBlockingQueue as a completion queue.<br />

ExecutorCompletionService(Executor executor,<br />

BlockingQueue completionQueue) Creates an<br />

ExecutorCompletionService using the supplied executor for base task execution<br />

and the supplied queue as its completion queue.<br />

58<br />

PdR_09010 - Stefano Millozzi


Esempio: Decomposizione Parallela<br />

Sono dati un certo numero <strong>di</strong> Solver per un dato<br />

problema, ciascuno restituisce un valore <strong>di</strong> tipo Result<br />

Si vuole ottenere una soluzione eseguendoli<br />

concorrentemente<br />

Il problema si considera risolto non appena si ottiene la<br />

prima soluzione non nulla<br />

Si interrompono tutti i risolutori ancora attivi<br />

Si usano<br />

59<br />

ExecutorCompletionService<br />

Executor<br />

Future<br />

PdR_09010 - Stefano Millozzi


Risolutore Concorrente (1)‏<br />

void solve(Executor e,<br />

Collection solvers)‏<br />

throws InterruptedException {<br />

CompletionService ecs =<br />

new ExecutorCompletionService(e);<br />

int n = solvers.size();<br />

List futures = new<br />

ArrayList(n);<br />

Result result = null;<br />

// esecuzione concorrente dei solver a seguire<br />

if (result != null)‏<br />

use(result); // utilizzo del risultato<br />

}<br />

60<br />

PdR_09010 - Stefano Millozzi


Risolutore Concorrente (2)‏<br />

…<br />

for (Callable s : solvers)‏<br />

futures.add(ecs.submit(s));<br />

for (int i = 0; i < n; ++i) {<br />

try { Result r = ecs.take().get();<br />

if (r != null) {<br />

result = r;<br />

break;<br />

}<br />

} catch(ExecutionException ignore) {}<br />

}<br />

}<br />

finally {<br />

for (Future f : futures)‏<br />

f.cancel(true);<br />

}<br />

// utilizzo <strong>di</strong> result<br />

61<br />

PdR_09010 - Stefano Millozzi

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!