Newsgroup: it.comp.java
Titolo: Sincronizzare due Thread su due oggetti diversi

Ciao a tutti,
        ho un grosso problema.
Ho due classi che ad un certo punto lanciano un thread ciascuna.
Ognuno di questi due thread deve accedere in maniera concorrente a due
liste di stringhe diverse(una nella sua classe base e l'altra nella
classe base dell'altro thread), facendo operazioni di lettura/
scrittura. Come posso garantire la concorrenza nell'accesso a queste
liste in modo da poter garantire l'assenza di deadlock?
Grazie, ciao

 wrote

Questo sarebbe un grosso problema ;-)
Con un codice cosi' semplice: due classi, due threads, e due liste di 
immutables (le stringhe sono immutables) e' davvero semplice garantire che 
non possa avvenire un deadlock. Per fare cio' basta che tu sappia quali sono 
le condizioni necessarie per causare un deadlock e tutto quello che devi 
fare e' assicurarti che non ci siano nel tuo codice.
Ti faccio un esempio banale, se tu usassi un lock solo non si potrebbe per 
definizione verificare alcun deadlock. Se vuoi usare due locks assicurati 
che, se devi acquisirli entrambi (e non uno per volta) essi vengano sempre 
acquisiti nello stesso ordine da entrambi i threads.
Ti consiglio di provare a scrivere del codice e magari postarlo se vuoi 
avere la conferma che sia deadlock-free.
Ad ogni modo ci sarebbero domande da porti:
- che operazioni di lettura/scrittura
- in un thread, devi accedere ad entrambe le liste in modo "atomico" o puoi 
accere prima ad una e poi all'altra
- devi solo avere una soluzione corretta e deadlock free oppure devi cercare 
di ottenere meno contention possibile.
- suppongo sia un esercizio che fa parte di un corso java e quindi devi 
attenerti a quanto richiesto oppure (anche se non credo) e' un problema 
reale nel qual cosa si puo' trovare una soluzione meno da esercizio e piu' 
adatta a codice che deve girare su sistemi veri.
Tommy


Ciao Tommy,
       grazie per la risposta. In effetti ho cercato di rendere
semplice la domanda ma forse nn sono riuscito nel mio intento.
Cercherò di essere più preciso.
Ho un'applicazione RMI che simula la connessione tra stazioni
ferroviarie. C'è una stazione centrale connessa a tutte le
periferiche, ed ogni periferica è connessa solo alle stazioni ad essa
adiacenti(le connessioni sono realizzate tramite l'uso di RMI Registry
inglobate nelle stazioni. Ogni stazione fa quindi un bind alla
centrale e uno ad ognuna delle sue periferiche). All'avvio della
simulazione i treni che connettono le stazioni periferiche partono per
le rispettive destinazioni, portandosi dietro dei passeggeri. e qui
arriva il nocciolo della questione. io devo evitare che due treni, che
viaggiano sullo stesso binario in senso opposto, si scontrino. Un
treno per il mio codice altro non è che un thread, il quale nella sua
run() si prende dei passeggeri da una coda, usa il riferimento
all'oggetto remoto per vedere se può passare nel binario, se puo
passare allora scarica i passeggeri nella coda della stazione remota
sempre usando il riferimento. Proprio qui sta il problema. Le stazioni
che generano questi treni che si possono scontrare sono n, e se ognuno
va a loccare una sua variabile, mi trovo appunto in una situazione di
deadlock. pensavo che se ci fosse stato un metodo per sincronizzarsi
su due oggetti(uno locale e uno remoto) contemporaneamente sarei
riuscito a risolvere la contesa, ma secondo me nn è possibile. La
soluzione che mi viene in mente è questa. La centrale, quando
trasmette l'impulso al sistema di avviarsi, se trova due treni che
potenzialmente si possono scontrare (p.e. A->B e B->A), in maniera
randomica setta per uno dei due una sorta di semaforo verde a partire
per primo. a quel punto, quando i thread andranno a vedere se possono
avviarsi, solo il thread che ha il verde parte, e l'altro aspetta una
notifica dell'arrivo. a notifica arrivata, parte anche lui.
Pensavo di fare una cosa del genere:
A: ha il verde , B: rosso
PseudoCodice delle stazioni periferiche:
while(ho il rosso){
     this.wait();
}
avvioTreno();
oggetto_remoto.notify();
Pseudocodice di avvioTreno:
public synchronized void avvioTreno() {
  metto a rosso la mia var del treno.
  metto a verde la var dell'oggetto remoto.
  mando il treno che scarica i passeggeri nella stazione adiacente.
}
Cosi in teoria solo uno dei 2 thread si mette in wait, mentre l'altro
esegue. risultato: un treno usa il binario, e l'altro attende che il
binario sia libero.
Che ne dici? dimenticavo. una delle condizioni è proprio che la
stazione centrale nn possa mettere lock nei binari.
Ciao e grazie.
            kr4b1t

 wrote
In questo modo se una stazione vuole mandare due treni di fila non riesce, 
deve prima ricevere un treno.
Visti i requisiti poco reali e molto da esercizio non mi viene in mente 
molto...
E se ogni tratta avesse una direzione primaria ed una direzione inversa ed 
il responsabile della tratta tra due stazioni fose sempre la stazione di 
arrivo?
In questo modo per viaggare da A a B non dovresti prendere due 
locks/semafori, uno per A ed uno per B, ma solo il lock della tratta A <-> B 
che si trova in B (perche' la direzione principale e' A -> B).
Ad esempio, dato:
- stazione periferica A
- stazione periferica B
- stazione periferica C
- stazione X (centrale)
Ti trovi con le varie tratte:
A <-> B
B <-> C
A <-> X
B <-> X
C <-> X
Ogni tratta viene descritta tramite apposita istanza di classe nel modo 
seguente:
Tratta 1:
---------
Stazione1:  A
Stazione2:  B
etc...
In questo caso la direzione principale e' A -> B e quindi il responsabile 
per il lock della tratta sarebbe B.
Quindi avresti:
A -> B (e direzione inversa, responsabile B)
B -> C (e direzione inversa, responsabile C)
X -> A (e direzione inversa, responsabile A)
X -> B (e direzione inversa, responsabile B)
X -> C (e direzione inversa, responsabile C)
In questo caso nota come B e' responsabile di due tratte: A->B e X->B. 
Quindi quando fai una richiesta per ottenere in modo esclusivo una tratta 
(ad esempio con un binary semaphore, o altro), devi specificare quale tratta 
(oppure guardi semplicemente il mittente della richiesta... e' A o X?).  Se 
il semaforo e' gia' a 0 allora la funzione RMI dira': no, non puoi ottenere 
la tratta (o qualunque altra cosa, bloccare, notificare quando diventa 
disponibile, etc..). Se invece il semaforo e' a 1 allora ritornera' con 
risultato positivo. Quindo il "mittente" ha finito ed il treno e' arrivato a 
destinazione il semaforo verra re-incrementato.
Nota che mettendo la stazione di arrivo come responsabile, X non e' mai 
responsabile di gestire i lock per i "binari" (cosa che ti proibisce 
l'esercizio).
Ogni thread (treno) non dovrebbe mai ottenere piu' di un lock/semaforo per 
volta in modo che eviti qualunque problema. Una stazione invece dovrebbe 
esser in grado di ottenere (tramite due threads/treni) piu' semafori per 
volta. Ad esempio X puo' avere un treno che va verso A ed un altro che va 
verso B contemporaneamente.
Ammetto di non averci pensato molto... magari e' una schifezza.
Spero possa essere di aiuto,
Tommy