Leggi l'articolo completo su flaviocdc.net
Il Nested Monitor Problem (NMP) si ha in genere quando un Thread entrando nella coda di wait di un oggetto “mette a dormire con se” il lock dell'unico oggetto che può “risvegliare” tale Thread tramite una notify*().
In generale è buona norma
Prima di entrare nella coda di wait di un oggetto X, un generico Thread T ne deve acquisire il lock intrinseco (senza se e senza ma), una volta entrato in wait tale lock viene rilasciato e messo a disposizione di altri Thread. Fin qua niente di nuovo, tuttavia se leggiamo attentamente non ho scritto che
La Nested in Nested Monitor Problem deriva dal fatto che la classe che esegue la wait di norma è una classe interna/annidata della classe che espone i metodi ai client; questa di norma è una buona tecnica di confinamento che limita alcuni problemi di concorrenza anche se, come abbiamo visto, può generare probkemi ben più gravi.
' Versione da riguardare
'
' A cura di Flavio Casadei Della Chiesa
'
Il Nested Monitor Problem (NMP) si ha in genere quando un Thread entrando nella coda di wait di un oggetto “mette a dormire con se” il lock dell'unico oggetto che può “risvegliare” tale Thread tramite una notify*().
In generale è buona norma
'non
' mettere un Thread in una
coda di wait se non si è sicuri che quancun altro Thread sia in grado di
eseguire una notify* su di essa (e quindi liberare i Thread in attesa).
A volte nonostante sia stato predisposto un adeguato sistema di
metodi di notifica e attesa può accadere che il metodo di notifica non
possa essere eseguito in quanto il suo accesso è “sbarrato” da un lock
che non potrà mai essere acquisito. Vediamo un esempio di NMP
class Inner { protected boolean cond = false; public synchronized void await () { while (!cond) try { wait (); } catch (InterruptedException e) {} // ..... } public synchronized void signal (boolean c) { cond = c; notifyAll (); } } class Outer { protected Inner inner_ = new Inner (); // nota! non è accessibile all'esterno public synchronized void process () { inner_.await (); } public synchronized void set (boolean c) { inner_.signal (c); } }Nella classe denominata Inner i metodi di attesa e notifica sono giustamente messi pubblici e synchronized; la classe Outer si limita solamente ad utilizzare tali metodi in blocchi sincronizzati al fine di evitare accessi concorrenti. A prima vista sembrerebbe tutto a posto tuttavia in questo frammento di codice si cela un'insidia poco visibile.
Prima di entrare nella coda di wait di un oggetto X, un generico Thread T ne deve acquisire il lock intrinseco (senza se e senza ma), una volta entrato in wait tale lock viene rilasciato e messo a disposizione di altri Thread. Fin qua niente di nuovo, tuttavia se leggiamo attentamente non ho scritto che
'tutti
' i lock acquisiti
dal Thread T vengono rilasciati; ed infatti solo il lock intrinseco
viene rilasciato, gli altri no. Se osserviamo il NMP da vicino si nota
che per un Thread T1 che invoca Outer.process() acquisisce ben due lock:
quello di Outer e quello di Inner, ma una volta entrato in wait (su
inner) il lock di Outer rimane acquisito e non rilasciato. Ogni altro
Thread T2 che prova ad invocare Outer.set() rimane bloccato
all'infinito in quanto il lock di Outer è in possesso di T1. Non esiste
quindi alcun modo per riacquisire il lock di Outer in quanto nessun
Thread è in grado di eseguire la notify().
La Nested in Nested Monitor Problem deriva dal fatto che la classe che esegue la wait di norma è una classe interna/annidata della classe che espone i metodi ai client; questa di norma è una buona tecnica di confinamento che limita alcuni problemi di concorrenza anche se, come abbiamo visto, può generare probkemi ben più gravi.
public class Outer { protected Nested _nested = new Nested (); // nota! non è accessibile all'esterno public synchronized void process () { _nested.await (); } public synchronized void set (boolean c) { _nested.signal (c); } private class Nested { protected boolean cond = false; public synchronized void await () { while (!cond) try { wait (); } catch (InterruptedException e) {} // ..... } public synchronized void signal (boolean c) { cond = c; notifyAll (); } } }
Nested Monitor Solution?
Beh le soluzioni sono molte e non tutte applcabili in ogni contesto.
Analizziamo una ipotetica soluzione ne propongo una basata su classi
annidate (nested)
Attenzione quindi a chiamare metodi “alieni” con un lock acquisito!
Propongo due versioni alternative di una ipotetica soluzione, presto seguiranno commenti … spero pure i vostri! Contattatemi pure all'indirizzo fcasadei CHIOCCIOLA gmail PUNTO com
public class NestedMonitor { private final Nested _nested = new Nested (); // nota! non è accessibile all'esterno public void process () { _nested.await (); // nessun lock acquisito } public void set (boolean c) { _nested.signal (c); // nessun lock acquisito } private class Nested { protected boolean cond = false; public synchronized void await () { while (!cond) try { wait (); } catch (InterruptedException e) {Thread.currentThread().interrupt(); return ;} // ..... } public synchronized void signal (boolean c) { cond = c; notifyAll (); } } }Come si nota, la soluzione consiste nel rimuovere la keyword synchronized dai metodi della classe Outer, più qualche “aggiunta” ….. Non si notano a prima vista grossi problemi, ma riflettendo meglio non è esclusa la possibilità di generare il solito problema nel caso in cui una classe cliente tenti un client side locking su una istanza della classe Nestedmonitor.
Attenzione quindi a chiamare metodi “alieni” con un lock acquisito!
Propongo due versioni alternative di una ipotetica soluzione, presto seguiranno commenti … spero pure i vostri! Contattatemi pure all'indirizzo fcasadei CHIOCCIOLA gmail PUNTO com
public class Outer { private final Nested _nested = new Nested (); // nota! non è accessibile all'esterno private final Object lock = new Object(); public void process () { _nested.await (); // nessun lock acquisito } public void set (boolean c) { _nested.signal (c); // nessun lock acquisito } private class Nested { protected boolean cond = false; public void await () { synchronized (lock) { //prendo il lock "giusto" while (!cond) try { lock.wait (); } catch (InterruptedException e) {Thread.currentThread().interrupt(); return ;} // ..... } } public void signal (boolean c) { synchronized (lock) { cond = c; lock.notifyAll (); } } } }
public class NestedOuter { private final Nested _nested = new Nested (); // nota! non è accessibile all'esterno public void process () { _nested.await (); // nessun lock acquisito } public void set (boolean c) { _nested.signal (c); // nessun lock acquisito } private class Nested { protected boolean cond = false; public void await () { synchronized (NestedOuter.this) { //prendo il lock "giusto" while (!cond) try { NestedOuter.this.wait (); } catch (InterruptedException e) {Thread.currentThread().interrupt(); return ;} // ..... } } public void signal (boolean c) { synchronized (NestedOuter.this) { cond = c; NestedOuter.this.notifyAll (); } } } }
Nessun commento:
Posta un commento