Sincronizzazione con Java
Corse critiche e sincronizzazione
•
•
Java implementa un meccanismo simile al monitor per
garantire la sincronizzazione fra thread
Ogni oggetto ha un lock associato ad esso
– Nelle classi possono essere definiti metodi synchronized
synchronized int myMethod()
– Un solo thread alla volta può eseguire un metodo synchronized
– Se un oggetto ha più di un metodo sincronizzato, comunque uno
solo può essere attivo
• NB: non vi è nessun controllo su metodi non sincronizzati
Sistemi Operativi 16/17
Sincronizzazione con Java
2
Metodi synchronized
•
•
•
La chiamata di un metodo sincronizzato richiede il “possesso”
del lock
Se un thread chiamante non possiede il lock (un altro thread
ne è già in possesso), il thread viene sospeso in attesa del
lock
Il lock è rilasciato quando un thread esce da un metodo
sincronizzato
lock
owner
Thread in attesa del lock
Sistemi Operativi 16/17
Sincronizzazione con Java
3
Metodi synchronized
synchronized void incrementaContatore() {
int tmp = contatore + 1;
if(tmp>maxContatore) maxContatore = tmp;
aspetta(milliAspetta);
contatore = tmp;
}
void synchronized decrementaContatore() {
int tmp = contatore;
aspetta(milliAspetta);
contatore = tmp - 1;
}
Sistemi Operativi 16/17
Sincronizzazione con Java
4
Sincronizzazione
•
Un Thread può decidere di non proseguire l’esecuzione
– Può allora volontariamente chiamare il metodo wait all’interno di
un metodo sincronizzato
• chiamare wait in un contesto diverso genera un errore
Sistemi Operativi 16/17
Sincronizzazione con Java
5
Il metodo wait()
 Quando un thread esegue wait(), si verificano i seguenti
eventi:
1. il thread rilascia il lock
2. il thread viene bloccato
3. il thread è posto in una coda di attesa
4. gli altri thread possono ora competere per il lock
lock
owner
Thread in bloccati (wait)
Thread in attesa del lock
Sistemi Operativi 16/17
Sincronizzazione con Java
6
Il metodo notify()

Nel momento in cui una condizione che ha provocato una
attesa si è modificata si può riattivare un singolo thread in
attesa tramite notify

Quando un thread richiama notify():
1)
2)
3)
viene selezionato un thread arbitrario T fra i thread bloccati
T torna fra i thread in attesa del lock
T diventa Eseguibile
Sistemi Operativi 16/17
Sincronizzazione con Java
7
insert() con wait/notify
public synchronized void insert(Object item) {
if(count==BUFFER_SIZE) {
try {
wait();
} catch(InterruptedException e) {}
}
++count;
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
if(count==1) notify();
}
Sistemi Operativi 16/17
Sincronizzazione con Java
8
Sincronizzazioni multiple
•
•
•
•
notify() seleziona un thread arbitrario.
Non sempre questo è il comportamento desiderato.
Java non permette di specificare il thread che si vuole
selezionare
notifyAll() “risveglia” tutti i thread in attesa. In questo modo si
permette ai thread stessi di decidere chi deve proseguire.
notifyAll() è una strategia conservativa utile quando più thread
sono in attesa
Sistemi Operativi 16/17
Sincronizzazione con Java
9
insert() con wait/notifyAll
public synchronized void insert(Object item {
while(count==BUFFER_SIZE) {
try {
wait();
} catch(InterruptedException e) {}
}
++count;
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
if(count==1) notifyAll();
}
private int count=0;
Sistemi Operativi 16/17
Sincronizzazione con Java
10
Confronto Monitor / Java
•
•
•
•
•
Monitor
Un solo processo può entrare
nel monitor ed eseguire una sua
procedura
Variabili condizione
wait(variabile_condizione)
signal(var. condizione)
Sistemi Operativi 16/17
Sincronizzazione con Java
•
•
•
•
•
(Qualunque) oggetto
Un solo thread alla volta
può eseguire uno dei
metodi synchronized
Una sola implicita (this)
this.wait()
this.notify() / notifyAll()
11
remove() con wait/notify
public synchronized Object remove() {
while(count==0) {
try {
wait();
} catch(InterruptedException e) {}
}
--count;
Object item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
if(count==BUFFER_SIZE-1) notify();
return item;
}
Sistemi Operativi 16/17
Sincronizzazione con Java
13
Metodo sleep
 static void sleep( millisecondi )
 Il Thread si sospende (non richiede l’uso del processore) per un
certo numero di millisecondi
 Nel frattempo possono andare in esecuzione thread a bassa
priorità
 NB
 sleep non restituisce il possesso del lock
 Sleep è un metodo statico della classe Thread
 Wait è un metodo della classe Object
Sistemi Operativi 16/17
Sincronizzazione con Java
15
Sincronizzazione di blocco
•
Si definisce scope di un lock è il tempo fra la sua acquisizione
ed il suo rilascio
•
Tramite la parola chiave synchronized si possono indicare
blocchi di codice piuttosto che un metodo intero
•
Ne risulta uno scope generalmente più piccolo rispetto alla
sincronizzazione dell'intero metodo
Sistemi Operativi 16/17
Sincronizzazione con Java
16
Sincronizzazione di blocco
Object mutexLock = new Object();
public void someMethod() {
nonCriticalSection_1();
synchronized(mutexLock) {
criticalSection();
}
nonCriticalSection_2();
}
Sistemi Operativi 16/17
Sincronizzazione con Java
17
Sincronizzazione di blocco
public class TestRunnableSync implements Runnable {
int count;
public void run() {
int tmp;
synchronized (this) { tmp = count++; }
String name = Thread.currentThread().getName();
System.out.println(name + " " + tmp);
}
public static void main(String[] args) {
int n=args.length>0?Integer.parseInt(args[0]):10;
Runnable runnable = new TestRunnableSync();
for(int i=0; i<n; i++) {
Thread th = new Thread(runnable);
th.start();
}
}
}
Sistemi Operativi 16/17
Sincronizzazione con Java
18
Versione non sincronizzata
public class TestRunnableSync implements Runnable {
int count;
public void run() {
int tmp;
tmp = count++;
String name = Thread.currentThread().getName();
System.out.println(name + " " + tmp);
}
public static void main(String[] args) {
int n=args.length>0?Integer.parseInt(args[0]):10;
Runnable runnable = new TestRunnableSync();
for(int i=0; i<n; i++) {
Thread th = new Thread(runnable);
th.start();
}
}
}
Sistemi Operativi 16/17
Sincronizzazione con Java
19
TestRunnableSync
TestRunnableSync
Un solo oggetto Runnable
count
Thread
Thread
Thread
Thread
...
N oggetti Thread
Sistemi Operativi 16/17
Sincronizzazione con Java
20
Sincronizzazione di blocco
public class TestRunnableSync2 implements Runnable {
int count;
public void run() {
int tmp;
synchronized (this) { tmp = count++; }
String name = Thread.currentThread().getName();
System.out.println(name + " " + tmp);
}
public static void main(String[] args) {
int n=args.length>0?Integer.parseInt(args[0]):10;
for(int i=0; i<n; i++) {
Runnable runnable = new TestRunnableSync();
Thread th = new Thread(runnable);
th.start();
}
}
}
Sistemi Operativi 16/17
Sincronizzazione con Java
21
TestRunnableSync2
N oggetti Runnable
TestRunnableSync
TestRunnableSync
TestRunnableSync
count
count
count
Thread
Thread
Thread
TestRunnableSync
...
count
Thread
...
N oggetti Thread
Sistemi Operativi 16/17
Sincronizzazione con Java
22
Daemon Threads
•
Sono thread
– Che effettuano operazioni ad uso di altri thread
• Per esempio: Garbage collector
– Sono eseguiti in background
• Usano cioè il processore solo quando altrimenti il tempo macchina
andrebbe perso
– A differenza degli altri thread non impediscono la terminazione di
una applicazione
• Quando sono attivi solo thread daemon il programma termina
– Occorre specificare che un thread è un daemon prima
dell’invocazione di start
- setDaemon( true );
– Il metodo isDaemon
• Restituisce true se il thread è un daemon thread
Sistemi Operativi 16/17
Sincronizzazione con Java
23
La classe Timer
•
La classe java.util.Timer permette la schedulazione di processi
nel futuro (attenzione esiste anche la classe javax.swing.Timer
dal comportamento parzialmente diverso)
Costruttore: Timer()
void schedule(TimerTask task, long delay, long period)
– Permette di eseguire un task periodico dopo un ritardo specificato
– Esistono altri costruttori e altri metodi (si veda
scheduleAtFixedRate )
Sistemi Operativi 16/17
Sincronizzazione con Java
25
La classe TimerTask
•
TimerTask è una classe astratta occorre estenderla e definire il
metodo run (ciò che il task deve fare)
long periodo = 1000; // per esempio un secondo
java.util.Timer timer = new java.util.Timer();
timer.schedule(new MyTimerTask(), 0, periodo);
public class MyTimerClass extends
java.util.TimerTask {
public void run() {
// ciò che devo fare
}
dovrebbe
richiedere un
tempo
inferiore a
periodo
}
Sistemi Operativi 16/17
Sincronizzazione con Java
26
Il barbiere dormiente
•
•
•
Sistemi Operativi 16/17
Soluzione java con Lock e Condition
È possibile definire più condizioni
– wait → await
– notify → signal
Cosa accade se i barbieri sono più di uno?
IPC
42
Soluzione
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SleepingBarberUsingReentrantLock extends Thread {
public static final int CHAIRS = 5;
public int numberOfFreeSeats = CHAIRS;
// customers waiting for their hair cut.
public Customers customers = new Customers();
// a ReentrantLock for barber with a condition
// to wait if the barber is not available
public Lock barber = new ReentrantLock();
public Condition barberAvailable = barber.newCondition();
// ReentrantLock for chairs so that we can increment the counter safely
public Lock accessSeats = new ReentrantLock();
Sistemi Operativi 16/17
IPC
43
Soluzione
void customer() {
accessSeats.lock(); // tries to get access to the chairs
if (numberOfFreeSeats > 0) { // if there are any free seats
System.out.println(getName() + " just sat down.");
numberOfFreeSeats--; // sitting down on a chair
customers.releaseCustomer(); // notify the barber that there is a customer
accessSeats.unlock(); // don't need to lock the chairs
barber.lock(); // we have to wait if the barber is busy
try {
barberAvailable.await();
} catch (InterruptedException e) {
} finally {
barber.unlock();
}
get_haircut(); // cutting...
} else { // there are no free seats
System.out.println("There are no free seats. " + getName() + " has left the
accessSeats.unlock(); // release the lock on the seats
}
}
Sistemi Operativi 16/17
IPC
44
Soluzione
void barber() {
while (true) {
try {
// tries to acquire a customer - if none is available he goes to sleep
customers.acquireCustomer();
// at this time he has been awaken
// want to modify the number of available seats
accessSeats.lock();
numberOfFreeSeats++; // one chair gets free
barber.lock();
try {
barberAvailable.signal(); // the barber is ready to cut
} finally {
barber.unlock();
}
accessSeats.unlock();
cutHair(); // cutting...
} catch (InterruptedException ex) {}
}
}
Sistemi Operativi 16/17
IPC
45
Problemi di IPC
•
•
•
•
•
Implementare una “barriera”
Implementare lo scambio di messaggi
Implementare un algoritmo di scheduling (decidere cioè quale
processo può continuare l’elaborazione)
Realizzare un semaforo stradale
Studiare un algoritmo di allocazione di memoria (processi che
chiedono e rilasciano memoria, un processo che la gestisce)
Sistemi Operativi 16/17
Sincronizzazione con Java
56
SleepingBarberUsingReentrantLock.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
09/04/2017
public class SleepingBarberUsingReentrantLock extends Thread {
public static final int CHAIRS = 5;
public int numberOfFreeSeats = CHAIRS;
// a Customer pool which will be waiting for their hair cut.
public CustomerPool customers = new CustomerPool(CHAIRS);
// a ReentrantLock for barber with a condition to wait if the barber is not
available
public Lock barber = new ReentrantLock();
public Condition barberAvailable = barber.newCondition();
// ReentrantLock for chairs so that we can increment the counter safely
public Lock accessSeats = new ReentrantLock();
class Customer extends Thread {
public Customer(int i) {
super("Customer-" + i);
}
public void run() {
accessSeats.lock(); // tries to get access to the chairs
if (numberOfFreeSeats > 0) { // if there are any free seats
System.out.println(getName() + " just sat down.");
numberOfFreeSeats--; // sitting down on a chair
1
SleepingBarberUsingReentrantLock.java
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
}
}
09/04/2017
customers.releaseCustomer(); // notify the barber that there is a
customer
accessSeats.unlock(); // don't need to lock the chairs
barber.lock();
try {
// now it's this customers turn but we have to wait if the barber
is busy
barberAvailable.await();
} catch (InterruptedException e) {
} finally {
barber.unlock();
}
get_haircut(); // cutting...
} else { // there are no free seats
System.out.println("There are no free seats. " + getName() + " has
left the barbershop.");
accessSeats.unlock(); // release the lock on the seats
}
/* this method will simulate getting a hair-cut */
public void get_haircut() {
System.out.println(getName() + " is getting his hair cut");
try {
sleep(5050);
} catch (InterruptedException ex) {}
}
2
SleepingBarberUsingReentrantLock.java
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
09/04/2017
class Barber extends Thread {
public void run() {
while (true) {
try {
// tries to acquire a customer - if none is available he goes to
sleep
customers.acquireCustomer();
// at this time he has been awaken -> want to modify the number of
available seats
accessSeats.lock();
numberOfFreeSeats++; // one chair gets free
barber.lock();
try {
barberAvailable.signal(); // the barber is ready to cut
} finally {
barber.unlock();
}
accessSeats.unlock();
cutHair(); // cutting...
} catch (InterruptedException ex) {}
}
}
/* this method will simulate cutting hair */
public void cutHair() {
System.out.println("The barber is cutting hair");
try {
sleep(5000);
} catch (InterruptedException ex) {}
3
SleepingBarberUsingReentrantLock.java
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
}
09/04/2017
}
public static void main(String args[]) throws InterruptedException {
new SleepingBarberUsingReentrantLock(args.length==0 ? 16 : Integer.
parseInt(args[0]));
}
SleepingBarberUsingReentrantLock(int size) throws InterruptedException {
Barber barber = new Barber();
barber.start();
Customer customers[] = new Customer[size];
for (int i=0; i<customers.length; i++) {
customers[i] = new Customer(i);
customers[i].start();
sleep(1000+(int) (2000*Math.random()));
}
for (int i=0; i<customers.length; i++) {
customers[i].join();
}
System.exit(0);
}
static class CustomerPool {
private
private
private
private
final Lock lock = new ReentrantLock();
final Condition poolAvailable = lock.newCondition();
int num_customers;
final int max_num_customers;
4
SleepingBarberUsingReentrantLock.java
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
public CustomerPool(int num_customer_pools) {
this.max_num_customers = num_customer_pools;
this.num_customers = 0;
}
public void acquireCustomer() throws InterruptedException {
lock.lock();
try {
while (num_customers <= 0)
poolAvailable.await();
--num_customers;
} finally {
lock.unlock();
}
}
}
}
public void releaseCustomer() {
lock.lock();
try {
++num_customers;
poolAvailable.signal();
} finally {
lock.unlock();
}
}
5
09/04/2017