Newsgroup: it.comp.java
Titolo: Synchronized e delega metodi

Ciao,
se ho una situazione come quella sotto, dove ho una classe che fa da
ponte verso un sottosistema dove devono esserci accessi synchronized
causa possibile accesso multithread, č giusto non mettere synchronized
alla classe LettoreScrittore? Credo di sě, ma sa'...
public class LettoreScrittore {
    private class Lettore {
        public synchronized void leggi() {};
    }
    private class Scrittore {
        public synchronized void scrivi() {};
    }
 
 
    private Lettore lettore = new Lettore();
    private Scrittore scrivi = new Scrittore();
 
    public leggi() {
    	lettore.leggi();    
    }
 
    public leggi() {
    	scrittore.scrivi();
    }
}    
Ciao.
-- 
Herbie

 alla classe LettoreScrittore? Credo di sì, ma sa'...
Dipende cosa vuoi sincronizzare.
Il codice sottostante, ad esempio, non è thread-safe se il problema è
sincronizzare lettore e scrittore, perchè non applichi il lock al
lettore mentre scrivi e viceversa.
Dovrebbero essere i metodi di LettoreScrittore ad essere synchronized,
o comunque è compito del delegante sincronizzare le operazioni.

 Dipende cosa vuoi sincronizzare.
Sì, anche tu hai ragione. 
Ho semplificato troppo, quello che volevo dire è che lettore e scrittore
modificano delle loro risorse non condivise l'un l'altro, insomma devono
essere sincronizzati singolarmente. Ho preso in effetti un esempio
pessimo, ma non voglio dire che leggono e scrivono dalla/sulla stessa
cosa.
Diciamo quindi che io mi trovi un lettore e scrittore già realizzati, e 
voglia solo creare un unico punto di accesso al sistema, mediante delega
di LettoreScrittore ...a quel punto, non devo sincronizzare due volte, no? 
-- 
Herbie

 di LettoreScrittore ...a quel punto, non devo sincronizzare due volte, no?
No, temo ahimè che non basti.
Il lock avviene a livello di oggetto. Ovvero, se il lettore ha metodi
sincronizzati, vuol dire che il thread mentre esegue quei metodi ha
accesso esclusivo garantito sull'oggetto lettore. Essendo lo scrittore
un'altro oggetto, non può essere garantito che un'altro thread non vi
acceda in concorrenza con lettore.

 writes:
H> Diciamo quindi che io mi trovi un lettore e scrittore già
H> realizzati, e voglia solo creare un unico punto di accesso al
H> sistema, mediante delega di LettoreScrittore ...a quel punto, non
H> devo sincronizzare due volte, no?
Come ti ha detto Vega, un lock sullo scrittore non blocca in attesa il
lettore e viceversa, direi che ti occorre un'oggetto terzo su cui fare
il  lock,   i.e.  un  Object   privato  di  LettoreScrittore   su  cui
sincronizzare gli accessi al lettore ed allo scrittore.
E no,  non è  il caso  di sincronizzare il  lettore sullo  scrittore e
viceversa... o per lo meno è MOOOLTO pericoloso...
-- 
 /\            ___
/___/\__|_|\_|__|___Gian Uberto Lauri_____________________
  //--\ | | \|  |   Integralista GNUslamico  
\/		    e allevatore di bug da competizione

 writes:
H> Diciamo quindi che io mi trovi un lettore e scrittore già
H> realizzati, e voglia solo creare un unico punto di accesso al
H> sistema, mediante delega di LettoreScrittore ...a quel punto, non
H> devo sincronizzare due volte, no?
GUL> E no, non è il caso di sincronizzare il lettore sullo scrittore e
GUL> viceversa... o per lo meno è MOOOLTO pericoloso...
Pericoloso beninsteso se si  lasciano i metodi sincronizzati così come
sono.  Altrimenti non cambia  nulla rispetto alla situazione attuale e
quindi non funziona come si vorrebbe....


 acceda in concorrenza con lettore.
Sì, ok, ma il fatto che dicevo è che, sebbene abbia scelto questo
esempio, non mi riferisco alla situazione produttore-consumatore.
Io ho un lettore che accede ai suoi dati, e uno scrittore che scrive su
dati indipendenti da quelli del lettore. Però, singolarmente, i due
devono essere thread-safe perché la gestione dei rispettivi dati non
deve avere interferenze.
A quel punto mi sembrava che se delego da A (LettoreScrittore) a B
(Lettore, p. es.), e ho un thread che chiama A.leggi non sincronizzato, 
e A.leggi chiama B.leggi synchronized, tutto andasse per il meglio, cioè
lo "sbarramento" (sincronizzazione) ci fosse nel punto dove serve
(B.leggi).
Il resto è stack... lo stack è per thread, no?
-- 
Herbie

 (B.leggi).
A scanso di equivoci, riepilogo come funziona la sincronizzazione.
Quando il thread entra in un blocco synchronized applica un lock
esclusivo all'intero oggetto, oppure se c'è già un lock si accoda in
attesa che venga rilasciato. Il lock ha effetto su tutte le richieste
sincronizzate su quell'oggetto, quindi se l'oggetto è lockato gli
altri thread si accodano a qualsiasi altro blocco synchronized mentre
eseguono senza ostacoli sia il codice non sincronizzato dello stesso
oggetto, sia qualsiasi codice, synchronized e non, di altri oggetti.
Quindi, nel luo caso: se A.leggi() chiama B.leggi() synchronized,
succede che nessun'altro thread può accedere in concorrenza a
B.leggi() e a tutti gli altri metodi synchronized di B. Però volevo
fosse chiaro che ne la classe A e ne la classe C (scrittore) sono
interessate da questo fatto, ovvero se un'altro thread chiama
A.scrivi() il quale delega ad C.scrivi() synchronized può farlo in
concorrenza con B.
Se vuoi evitare la concorrenza fra lettura e scrittura fatta da due
oggetti differenti, è inutile sincronizzare i singoli oggetti (non ha
effetto) ma è più utile sincronizzare il delegante che coordina le
chiamate. Naturalmente ponendo che lettore e scrittore siano privati e
quindi accessibili solo attraverso il delegante.
Spero di aver chiarito il concetto.

 concorrenza con B.
Sì, ok, allora avevo capito, e questo mi sta bene, perché mi interessava
introdurre lettoreScrittore solo come facciata per un sottosistema.
Volendo buttar giù delle semplici regole per decidere quando
sincronizzare un metodo, mi pare di aver capito che:
* se un metodo fa solo qualcosa su variabili locali che non si riferiscono 
  a oggetti già esistenti non c'è bisogno di sincronizzare, perché sta
  tutto nello stack dei singoli thread
* se un metodo A delega a un metodo B senza fare altro, a non deve
  essere sincronizzato, visto che, dopotutto, non modifica nessun
  oggetto ma delega questa azione a B, che si vedrà poi se deve essere
  sincronizzato o no
* bisogna sincronizzare in tutti i casi in cui un metodo, se interrotto
  dal multithreading, può lasciare l'oggetto in uno stato inconsistente,
  o se si legge qualche valore che può essere modificato nel frattempo,
  come nella creazione di istanze singole a livello di classe, tipico di
  un pattern singleton, per dire
Mi pare di aver capito, comunque, che anche se un metodo modifica un
campo con una assegnazione che pare atomica, vedi sotto, è il caso di
sincronizzare, perché mi pare di ricordare che assegnazioni di certi
tipi (double?) possono essere interrotte dal multithreading
es. 
public class Test {
	private double pippo;
 
	public synchronized void loSincronizzoOno() {
		pippo = 200;
	}
}
 
Ciao
-- 
Herbie