View Single Post
Old 10-07-2009, 17:24   #9
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Se ho due Thread A e B entrambi in esecuzione, A crea un Point - oggetto tipicamente mutabile - di coordinate (100, 75) e lo passa a B tout court il punto che B riceve può essere un qualsiasi valore tra un punto di coordinate (0,0), un punto di coordinate (100,0) e un punto (0,75). Questi sono i valori che B può legittimamente attendersi.

La sincronizzazione ci dice che se ho un monitor M e il Thread A assegna al punto le sue coordinate sincronizzandosi su M una successiva lettura del punto da parte di un Thread B sincronizzato su M legge necessariamente i valori precedentemente assegnati da A.

Nota bene: precedentemente. La sincronizzazione non è perpetua. Abbiamo un punto P. C'è un metodo che assegna delle coordinate a P sincronizzandosi su P stesso e poi delega la lettura di P all'EDT. Una cosa tipo:

Codice:
private final Point p = new Point();//final = p non è mai null

public void set(int x, int y) {
    synchronized(p) { p.x = x; p.y = y; }
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            synchronized(p) {
                leggi i valori p.x e p.y;
            }
        }
    });
}
Qui ho la garanzia che l'aggiornamento dei valori di p è visibile all'EDT ma non ho una garanzia di ordine. Il codice garantisce che se il Thread A dice:

set(1, 1);
set(2, 2);
set(3, 3);

l'edt può vedere una qualsiasi tripla composta dai punti ( (1,1), (2,2), (3,3) ) e non vedrà mai (0,1) o (0,0) o (1,0) o (0,2) o (3,1).

Vale a dire che non esiste una race condition riguardante i valori del punto ma esiste una race condition tra le invocazioni di set da parte del Thread A e le letture dell'EDT.

Per evitare anche la seconda race condition occorre generare un nuovo punto ad ogni invocazione di set e passare quel punto all'EDT come messaggio: così facendo ogni invocazione di set genera un nuovo punto e l'eventuale invocazione plurima di set da parte del Thread A non lede la lettura dei valori successivamente impostati da parte dell'EDT.

Codice:
private final Point p = new Point();

public synchronized void set(int x, int y) {
    p.x = x;
    p.y = y;
    final Point message = new Point();
    synchronized(message) {
        message.x = x;
        message.y = y;
    }
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            synchronized(message) {
                leggi message.x e message.y
            }
        }
    }
}
Rispetto a prima qui c'è un synchronized in più perchè "p" è supposto essere un campo mentre prima non c'erano campi. Cosa succede se un Thread invoca tre volte il metodo set adesso?

Succede che quel Thread crea tre punti diversi - uno ad ogni invocazione - ed ognuno di quei punti è passato all'edt per una lettura sincronizzata. Ergo ora se il Thread A dice:

set(1, 1);
set(2, 2);
set(3, 3);

l'Edt legge necessariamente:

(1,1)
(2,2)
(3,3)

Capita perchè rispetto ad A le tre invocazioni successive di set sono necessariamente ordinate e in più ognuna di queste crea un nuovo punto - e non agisce su un unico punto come prima.

Ma anche qui c'è race condition se anzichè parlare di un solo Thread A iniziamo ad avere due Thread, A e B, che invocano set. Qui occorrerà stabilire se esista un ordine necessario tra le invocazioni di set da parte di A e B e in caso affermativo si procederà a garantire tale ordine.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso