|
|
Soluzione
Esercizio 001 - Thread
L'esercizio
proposto lo scorso mese riguardava i thread di Java e chiedeva quali erano
tutti i possibili risultati che una qualsiasi JVM potesse generare in uscita
di 3 specifici casi.
Tabella
1: Soluzione totale e completa dell' Esercizio 001
Possibile
Stampa (i-j) |
Esempio
1
(senza
synchronized
e
senza volatile) |
Esempio
2
(con
synchronized) |
Esempio
3
(con
volatile) |
minimo
valore stampabile |
-n |
0 |
-n |
massimo
valore stampabile |
+m |
0 |
+1 |
con
n e m interi positivi grandi a piacere.
La
specifica formale di come si comportano thread, synchronized, lock, monitor
e volatile si può trovare in JLS,
mentre una definizione meno formale e più sintetica è riassunta
dai seguenti punti:
-
ogni programma
Java (o, meglio, ogni Java Virtual Machine JVM) possiede 1 memoria condivisa
(ad es. la RAM) per le variabili di tutti i thread e n aree locali (ad
es. registri) per gli n thread in esecuzione accedibili ciascuna solamente
all'n-esimo thread in esecuzione;
-
ogni thread
lavora sempre su copie di tutte le variabili di un
programma copiandole (quando più gli pare e piace ma comunque prima
del loro uso) dalla memoria condivisa alla memoria locale;
-
ogni thread
è libero di ricopiare (quando più gli pare e piace ma comunque
dopo il loro uso) le copie delle variabili che ha modificato dalla sua
area locale alla memoria condivisa del programma;
-
ogni thread
è obbligato a leggere il valore di una variabile volatile
dalla
memoria condivisa subito prima di un suo uso e di ricopiarla nella memoria
condivisa subito dopo il suo uso;
-
ogni thread
all'ingresso di una qualsiasi synchronized
è
obbligato a:
-
scrivere
tutte le variabili che ha modificato dalla sua memoria locale alla
memoria condivisa;
-
leggere
tutte le variabili di un programma dalla memoria condivisa alla
sua memoria locale;
-
ogni thread
all'uscita da una qualsiasi synchnonized
è
obbligato a scrivere nella memoria condivisa tutte le variabili che
ha modificato nella sua memoria locale;
-
la synchronized
su uno
stesso oggetto Obj è un punto di lock per tutti i thread che tentino
di eseguirla e quindi un solo thread alla volta può entrare in una
synchronized
e finché
questo thread non esce tutti gli altri thread su synchronized
dello
stesso oggetto Obj sono bloccati e non possono girare (eccetto che per
l'uso di wait
che però
in questo caso non ci interessa perché non usata nel testo dell'esercizio).
Le regole
2 e 3 si applicano all'esempio 1, la regola 4 si applica all'esempio 3,
mentre le rimanenti regole 5-7 spiegano il risultato dell'esempio 2.
Nota
1 : Questo esercizio è un classico esempio per verificare la
conoscenza dei thread da parte degli sviluppatori Java. Nell'ultimo anno
ho proposto questo test a varie decine di amici e colleghi e quasi tutti
mi hanno fornito -n..+1
(invece che -n..+m
) come risposta al primo esempio. È stata una risposta abbastanza
sconfortante dato che i thread sono un asse portante di Java (a differenza
di altri linguaggi, come il C o il C++ dove sono stati aggiunti a
posteriori al run-time system e non sono presenti nel linguaggio stesso
sin dalla sua fondazione) e la loro non-conoscenza può portare a
gravi problemi nelle applicazioni basati su di essi.
Nota
2: Contravvengo alle regole del gioco
che io stesso mi sono dato e vado a parlare un po' delle JVM attualmente
disponibili. Ebbene se provate sperimentalmente ad eseguire l'esercizio
potrete vedere che in quasi tutte le JVM la tabella dei risultati sperimentali
è la seguente:
Tabella
2: Soluzione parziale e incompleta dell'Esercizio 001
Possibile
Stampa (i-j) |
Esempio
1
(senza
synchronized
e
senza volatile) |
Esempio
2
(con
synchronized) |
Esempio
3
(con
volatile) |
minimo
valore stampabile |
0 |
0 |
0 |
massimo
valore stampabile |
+1 |
0 |
+1 |
Questa
tabella è ovviamente corretta, perché copre un sottoinsieme
di quella generica che è stata fornita all'inizio
di questo esercizio, ma non è completa. Se
i programmi Java che noi scriviamo si basassero su arbitrarie regole dedotte
dalla precedente tabella allora essi non sarebbero portabili sulle varie
piattaforme perché potrebbe sempre esistere delle JVM corrette che
fornirebbero un risultato inatteso (ad esempio: i-j=-27).
A
buon intenditor poche parole!
Regole
del gioco
I quiz
presentati in questa rubrica trattano esclusivamente del linguaggio Java,
così non sono richieste conoscenze di applets, servlets, Beans,
etc.. Le uniche classi predefinite che possano eventualmente essere utilizzate
sono quindi solo quelle appartenenti al package java.lang (classi come
ad esempio: Thread, Class, ClassLoader, System, Runtime, etc...). Per risolvere
i quiz non serve inoltre editare, compilare o testare nessun tipo di codice.
Si tratta di quiz mentali che devono essere risolti solo con la
testa e non con il computer. Quindi:
-
Non bisogna
compilare ed eseguire nessun pezzo di codice: non sono interessato a sapere
che con certe implementazioni della JVM si ottengono certi risultati mentre
con altre implementazioni altri risultati. Sono invece interessato a quali
siano l'insieme di risultati validi così come specificato dal linguaggio
Java: cioè quali sono i risultati che un buon programmatore Java
si può aspettare da una qualsiasi JVM che soddisfi le specifiche
di Java?
-
Non occorre
che inviate una soluzione del quiz a me o alla rivista. La soluzione sarà
fornita nel prossimo numero. Una volta nota la soluzione (quindi solo dal
prossimo mese in poi) potete allora eventualmente contestarla e/o commentarla
inviandomi
una email. Io sarò ben lieto di pubblicare e discutere tutti
i pareri ricevuti.
|
|
|
|