Progetto di reti di Calcolatori e Sistemi Informativi
Progetto di reti di Calcolatori e Sistemi Informativi
Progetto di reti di Calcolatori e Sistemi Informativi
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