Michele Genchi ha scritto:
Con notifyall tutti i thread avendo priorità di default 5 hanno la stessa probabilità di essere eseguiti una volta che il lock è stato rilasciato.
Con notify solo una avrà la possibilità di partire, che nel caso dello scheduler è quella scritta successivamente quindi è dipendente dal s.o.
Ho detto bene?
Qui non c'entra la priorità dei thread impostabile sul java.lang.Thread. La questione è più fine e probabilmente non ci sei ancora arrivato.
Ecco uno scenario di esempio (grazie anche allo sleep con tempo casuale che rende più arbitrarie le tempistiche).
Mettiamo che arriva per primo ad acquisire il lock ed eseguire il call il Caller <1>. Siccome 0 != 1 è true, entra nel while e va in sospensione perché giustamente non è il suo turno.
Poi mettiamo che arriva per secondo il Caller <2>, idem siccome 0 != 2 è true entra nel while e va in sospensione non essendo nemmeno il suo turno.
Poi arriva il Caller <0> e siccome 0 != 0 è false salta proprio il while (non ci entra nemmeno), quindi stampa le scritte e incrementa il currentIndex e poi "notifica".
Ora ecco la differenza tra notify e notifyAll.
Con notify() viene svegliato un solo thread e attenzione uno
a caso tra quelli nel "wait set" di quel lock. La documentazione ufficiale di notify() è molto chiara su questo: "
The choice is arbitrary and occurs at the discretion of the implementation."
Se è "a caso", allora potrebbe svegliare il thread sbagliato, ad esempio quello del Caller <2>. Se non ci fosse il ciclo sul wait, il Caller <2> proseguirebbe nella stampa ma non sarebbe concettualmente corretto, visto l'obiettivo.
Grazie al ciclo invece verrebbe ritestata la condizione che ora è 1 != 2 ed essendo true si rimetterebbe in attesa. Il risultato è che la sequenza si pianterebbe lì, il <1> è ancora in attesa e non può continuare e il <2> si è rimesso anch'esso in attesa.
La tua applicazione è completamente bloccata e non può proseguire nella sequenza!
Con notifyAll() invece tutti i thread in attesa nel wait set di quel lock vengono risvegliati. Qui
a maggior ragione serve il ciclo con il test.
Se anche il Caller <2> riesce a riacquisire il lock prima del <1>, ritesta la condizione e si rimette in attesa perché giustamente non è ancora il suo turno. Ma ora anche il <1> ha la possibilità di riacquisire il lock e ritestare la condizione e adesso è proprio il suo turno. Quindi poi tutto procede correttamente.
Questo è appunto uno dei motivi per cui il wait() dovrebbe sempre essere fatto dentro un ciclo che testa una condizione che deve "reggere" affinché il thread stia in attesa.
Michele Genchi ha scritto:
Allora del codice scritto perchè non capita mai l'eccezione qui:
Un altro thread dovrebbe fare un interrupt su uno dei tre thread. Ma non c'è una cosa del genere nel codice. Quindi perlomeno volutamente non c'è questo interesse/obiettivo.
Michele Genchi ha scritto:
e poi il comando return l'ho usato solo nel caso di un metodo che ritorna un valore...
Qui che fa?
Semplicemente fa ritornare il metodo, senza alcun valore (il metodo è void).