Newsgroup: it.comp.java
Titolo: Sincronizzare

Come risolvereste voi questo problema?
Premetto, sono il re dei niubbi in Java ed in genere nella OOP. Quindi,
portate pazienza per le castronerie che diro'.
Ho una classe, una cui istanza viene allocata da due task diversi.
Quindi, il task a esegue una new ed ottiene un handler, il task b fa
un'altra new, ed ottiene un altro handler.
Di fatto quindi son due oggetti identici, ma distinti.
All'interno di questa classe ho un metodo che vorrei far si che solo uno
alla volta possa eseguire. E questo deve varlere in assoluto. Cioe' se il
task a sta eseguendo sulla propria istanza il metodo pippo, il task b, che
lavora su una seconda istanza, se richiama pippo dev'essere messo in attesa,
per poi essere riavviato quando il pippo del task a ha finito.
Ho provato ad usare la synchronize, ma non funziona, credo perche' la
sincronizzazione avviene, da quel che ho capito, solo tra diversi thread che
accedono pero' alla medesima istanza dell'oggetto (e non e' il caso mio).
Ho provato a definrie classe e metodi statici, con la speranza cosi' di
avere una sola copia, ma non mi permette di compilare, dicendomi che da un
metodo statico non posso chiamare metodi non statici (ad esempio, la
wait() ).
Il metodo notifyAll non funziona, perche' anche qui lui sblocca, se non ho
capito male, tutti quei task in wait sulla stessa sua istanza, cosi' se il
task a sull'istanza a va in wait, ed il task b, sull'istanza b del medesimo
oggetto, da una notifyAll, il task a rimane comunque fermo e da li non si
muove (deadlock)
L'unica soluzione, ORRIBILE (ma che funziona), che ho trovato, e' la
seguente:
class Sincronia {
    private static boolean verde=true;
     synchronized void SemaforoRosso() {
     while (verde == false) {
            try {
                     wait(1000);
                } catch (InterruptedException e) {
                }
        }
        verde= false;
       }
    synchronized void SemaforoVerde() {
        verde= true;
    }
}
public class AccessoEsclusivo {
    public void metodo(String nome)
    {
        Sincronia Semaforo = new Sincronia();
        Semaforo.SemaforoRosso();
        /*
                 ... fai quel che devi fare ....
        */
        Semaforo.SemaforoVerde();
    }
}
In sostanza dichiaro una variabile statica che funge da semaforo. Per
settare il semaforo rosso, il metodo ne controlla prima il valore: se e'
gia' rosso (e qui sta la parte orribile) attende un secondo e riprova...
fino a quando trova verde.
C'e' un metodo piu' elegante? Sicuramente... quale?
Puo' aiutare forse spiegare l'ambito: sto scrivendo delle jsp che devono
accedere ad una risorsa non condivisibile (un database), e devo far si che
se due utenti, dal browser, cercano di accedere contemporaneamente, il primo
che arriva si prende la risorsa, l'altro invece attende finche' il primo ha
ottenuto il risultato della query, dopodiche' parte la sua interrogazione.
Grazie anticipatamente

 il caso mio). 
Come non e' il caso tuo?
Delle due l'una: o i due oggetti Task lavorano nello stesso thread, e 
allora il lavoro sara' sequenziale (se ovviamente crei un metodo lavora() 
che esegue i vari passi). Oppure no, e allora hai due thread che si 
sincronizzino sullo stesso oggetto, ad esempio
Object lock = new Object();
MioTask t1 = new MioTask(lock);
MioTask t2 = new MioTask(lock);
...associa t1 e t2 a due thread diversi e li fa partire...
e MioTask.lavora() sara' del tipo
sycnhronized(lock) {
 ..lavoro...
}
-- 
Fighting for peace is like fucking for virginity
ObjectZone - 
http://space.tin.it/computer/csadun

 C'e' un metodo piu' elegante? Sicuramente... quale?
L'idea e' buona, ma ci sono due errorini:
  - ogni volta viene creato un nuovo oggetto Semaforo dentro metodo.
  - si potrebbe usare il + elegante wait e notifyall
    invece dello sleep di un secondo.
Puoi prendere qualche spunto da:
http://www.mokabyte.it/mokaquiz/2001/11/soluzione.htm
- Dario

 ottenuto il risultato della query, dopodiche' parte la sua interrogazione.
Se non sono letture dovrebbe preoccuparsene in db di mantenere i lock,
ma forse non mi è chiaro il contesto.
Per quanto riguarda la sincronizzazione non posso esserti molto di aiuto
perché coi thread ci ho sempre litigato (e hanno quasi sempre vinto
loro) ma provo lo stesso a raccontarti quel poco che mi ricordo:
prima di entrare in un blocco synchronize(obj){} il thread di esecuzione
cerca di acquisire il lock dell'oggetto obj (se si tratta di un metodo 
synchronize il lock che si cerca di acquisire è quello dell'oggetto di
cui viene chiamato il metodo, se i due oggetti sono diversi i lock
sono diversi).
Prova a instanziare un oggetto obj e passalo ai due thread e al posto di
sincronizzare i metodi delimita la parte di codice necessaria
con synchronize(obj). (Se non vuoi passarlo come argomento puoi
definirlo come static della classe che estende Thread o implementa
Runnable).
I thread però non sono materia su cui i miei ricordi sono freschi, se
mi sbaglio correggetemi !
Ciao

 C'e' un metodo piu' elegante? Sicuramente... quale?
Ovviamente si, il trucco sta nell'utilizzare un synchronized statement 
(invece di un synchronized method). Nel tuo caso scriverei cosi':
public class AccessoEsclusivo {
     public void metodo(String nome)
     {
         synchronized(this.getClass()) {
                  ... fai quel che devi fare ....
         }
     }
}
Spieghiamo un po' le cose.
synchronized(oggetto) fa si che se due thread cercano di entrare nel 
blocco utilizzando lo stesso oggetto solo uno dei due ci riesca.
In pratica:
    public void synchronized metodo(...){ ... }
E' esattamente la stessa cosa di:
    public void metodo(...){
      synchronized(this){
        ...
      }
    }
Tu pero' non devi sincronizzarti su this, ma sul qualcosa che sia unico 
per tutte le instanze della classe.
Puoi operare in due modi: o hai un attributo statico (deve essere un 
riferimento, un tipo primitivo non va bene) e fai 
synchronized(attributo) oppure, semplicemente, utilizzi l'oggetto 
ritornato dal metodo getClass(), che e' un'istanza della classe Class ed 
e' quello su cui si sincronizzano i metodi statici.
Spero di essere stato chiaro,
Andrea Polci

     invece dello sleep di un secondo.
Ci ho provato ma non funziona..
Il punto e' che wait e notifyall vanno bene per tutti i task in attesa su un
monitor, ed ogni istanza di una classe ha il proprio monitor.
Cosi', se due utenti richiamano la medesima jsp, ho due task che eseguono
una new, creando due istanze, e due monitor.
Il wait sulla prima istanza sara' risvegliato dal notifyall eseguito sulla
medesima istanza, mentre invece sara' nel mio caso eseguito sulla seconda
istanza, lasciando di fatto bloccato per sempre il primo.
Provato, e ti assicuro che non funziona.

 sincronizzino sullo stesso oggetto, ad esempio
Come detto, l'ambito e' quello jsp: due utenti lanciano due jsp, quindi due
task separati. Ciascuno esegue una new, ottenendo la propria istanza.
Quindi ho due thread che accedono su due oggetti identici ma distinti. In
questo caso le sincronizzazioni non vanno.
La soluzione e' stata quella di rendere la classe statica (final), con un
po' di fatica. In questo caso l'oggetto e' unico, e synchronized funziona.

 ma forse non mi è chiaro il contesto.
Dovrebbe.. ma cosi' non e'.
In sostanza io per accedere al DB devo per forza ricorrere a delle funzioni
scritte in C presenti in una dll.
Le quali funzioni, ahime', non tollerano che vi siano accessi simultanei.
Cosi' se e' vero che il DB gestisce bene la concorrenza, e' anche vero che
puo' esservi un solo task alla volta che fa girare una funzione C di
interfaccia.
Queste funzioni le uso tramite JNI, e il problema avviene quando due browser
richiedono contemporaneamente di eseguire una query.

 Ovviamente si, il trucco sta nell'utilizzare un synchronized statement
Ti ringrazio per l'esauriente spiegazione.
Come detto, alla fine, son riuscito a dichiarare la classe come statica, ma
se in futuro dovessi aver rogne simili senza questa possibilita', terro'
buono il tuo insegnamento.
Messaggio in keep.
Grazie ancora.