Newsgroup: it.comp.java
Titolo: sincronizzazione threads e wait()....

Ciao a tutti,
da neofita di Java sto cercando di approfondire i meccanismi di 
sincronizzazione tra threads e del metodo wait(). In particolare ho 
fatto le 2 seguenti classi:
public class ThreadsTest {
     public static void main (String[] args) {
         new SimpleThread("A").start();
         new SimpleThread("B").start();
         new SimpleThread("C").start();
     }
}
public class SimpleThread extends Thread {
     public String str;
     public static boolean ok;
     int i;
     public SimpleThread(String str) {
         super(str);
         this.str = str;
     }
     public synchronized boolean isOk() {
         ok = true;
         notifyAll();
         return ok;
     }
     public void run() {
        synchronized (this){
         for (i = 0; i < 10; i++) {
          if (str == "B"){
              while (!ok) {
                 try {
                    sleep(2000);
                    wait();
                  } catch (InterruptedException e) {
                     e.printStackTrace();
                  } catch (IllegalMonitorStateException imst) {
                     imst.printStackTrace();
                  }
              }
           }
          if (i == 5) {
              isOk();
              System.out.println(str + ": " + ok);
          }
          System.out.println(i + " " + getName());
          long s = (long)(Math.random()* 1000);
          try {
                 sleep(s);
          } catch (InterruptedException e) {}
          }
         System.out.println("FATTO! " + getName());
         isOk();
      }
     }
}
Quello che volevo fare è semplicemente un test sull'uso del wait(): 
lancio 3 threads contemporanei ed uno solo di questi si mette in attesa 
(il "B") sino a quando non si verifica una certa condizione, cioè che 
almeno 1 degli altri 2 threads abbia raggiunto col suo contatore il 
valore 5.
Quello che accade (JVM di NetBeans) è che il thread in attesa non 
riparte MAI, e non capisco perché; ovviamente non lancia nessuna 
eccezione ed ovviamente è attivo.
Se provo a modificare il codice togliendo il wait() e mettendo un 
System.out.println della variabile booleana ok, ottengo che 
effettivamente la variabile viene stampata solo DOPO che la condizione è 
verificata, e viene stampata col suo valore TRUE, il che significa che 
il thread "B" è stato notificato correttamente del fatto che ok è 
passato da FALSE a TRUE ed è partito con la stampa solo a condizione 
avvenuta.
Qualcuno mi potrebbe avere idea di come mai non avviene il "risveglio" 
dal wait??
Tra l'altro l'uso del ciclo while e del wait in questo modo è 
consigliato (così ho letto) anche per un una questione di uso di cpu: 
senza il wait il loop sarebbe continuo, mentre col wait il loop verrebbe 
effetuato una sola volta sino al cambiamento di stato.
Ringrazio anticipatamente tutti quei volenterosi che avranno la bontà di 
aiutarmi!!

Nad ha scritto:


Il problema e' che hai ceffato la sincronizzazione.
Non ha senso sincronizzare su this all interno di una thread! Questo
vuol dire che ciascun oggetto thread e' possessore
del lock su cui il blocco e' sincornizzato.. quindi se hai 3 thread ci
sono 3 lock completamente indipendenti.
Ergo, la thread B fa una wait sul SUO lock, e nessun altra thread
potra' mai fare una notify SU QUEL LOCK. Quindi sta in wait per
sempre.
ps > IllegalMonitorStateException e' una Runtime, non farne la catch
( te l' ha fatto Netbeans in automatico ,eh?)

ricca...@email.it
 ha scritto:

Ciao Riccardo,
intanto GRAZIE per aver risposto, sei stato l'UNICO a farlo!
In effetti stavo sperimentando su quel blocco proprio perché non ho 
capito perfettamente il suo funzionamento. Come detto sono neofita della 
programmazione ad oggetti e di Java.
A questo punto ne approfitto e ti CHIEDO:
tu come lo faresti? Mi riferisco al fatto di lanciare più threads 
identici e far in modo che solo uno attenda sino al verificarsi di una 
condizione che in questo caso è che il contatore i sia arrivato sino a 5 
in almeno 1 degli altri thread.
Ho capito che il lock non può essere su i né sul proprio thread; se 
provo a creare un oggetto ad hoc che sarebbe il possessore del lock 
ottengo l'eccezione di runtime perché il thread "B" (per capirci, quello 
in wait) non è il possessore del lock nemmeno se lo lanciassi prima 
degli altri 2.
Sono sicuro che ci metterai 2 secondi a farlo ed io capirei bene il 
meccanismo.
Poi sono andato a vedere in dettaglio la documentazione del wait ed mi 
sono reso conto che solo InterruptedException è di tipo "checked" mentre 
le altre 2 estendono RuntimeException quindi non devo fare catch, come 
giustamemnte avevi detto.
NON è stato NetBeans a mettercelo ma (erroneamente) io perché avevo 
visto anche l'altra eccezione senza approfondire.
GRAZIE per l'eventuale ulteriore aiuto!!

Nad ha scritto:

Strano che tu non abbia avuto piu' risposte, spesso vedo gente
disposta ad aiutare qui...
si vede che sono in vacanza!
Eccoti servito, se ho capito bene quello che vorresti fare, ho usato
un conto alla rovescia invece di un contatore cresecente perche' mi
sembrava + divertente. e qualche println per verificare come "si
muove" il programma. Buono studio con Java!
import java.util.concurrent.TimeUnit;
public class ThreadsExample
{
	public static void main(String[] args)
	{
	  Object coordinator = new Object();
	  new NasaRocket(coordinator, "RocketA").start();
	  new NasaRocket(coordinator, "RocketB").start();
	  new NasaRocket(coordinator, "RocketC").start();
	  new NasaRocket(coordinator, "RocketD").start();
	}
}
class NasaRocket extends Thread
{
	private Object coordinator;
	private volatile static int countDown = 3;
    public NasaRocket(Object coordinator, String name)
    {
    	super(name);
    	this.coordinator=coordinator;
    }
	public void run()
	{
			synchronized (coordinator)
			{
				if (this.getName().equals("RocketD"))
			    {
			      while (countDown !=  0)
			      {
				     try
				     {
					    System.out.println(this.getName() + " va in wait " + "
countDown = " + countDown);
					    coordinator.wait();
				     }
				     catch (InterruptedException e)
				     {
					   e.printStackTrace();
				     }
				     System.out.println(this.getName() + " svegliata " + "
countDown = " + countDown);
			       }
			       System.out.println(this.getName() + " decolla! countDown = "
+ countDown + " we have liftoff!");
			    }
			}
			int sleeptime= (int) (Math.random() * 10000);
			try
			{
			   TimeUnit.MILLISECONDS.sleep(sleeptime);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			synchronized (coordinator)
			{
			   countDown--;
			   coordinator.notify();
			}
			System.out.println(this.getName() + " terminata");
	}
}

 muove" il programma. Buono studio con Java!
GRAZIE, non sai quanto sia stato utile x me vedere questo codice. Ho 
modificato di conseguenza quello mio dopo aver capito che:
1- deve esistere un OGGETTO (magari creato ad hoc) che deve gestire 
dall'alto la sincr di tutti i threads, dunque va passato a loro come 
parametro;
2- i metodi wait e notify(All) da richiamare sono quindi i metodi 
dell'oggetto e NON quelli dei thread.
Infatti ora FUNZIONA TUTTO ed il codice è il seguente:
[CODICE]
public class ThreeThreadsTest {
     public static void main (String[] args) {
         Object o = new Object();
         new SimpleThread("A", o).start();
         new SimpleThread("B", o).start();
         new SimpleThread("C", o).start();
     }
public class SimpleThread extends Thread {
     public String str;
     public static boolean ok;
     int i;
     public Object o;
     public SimpleThread(String str, Object o) {
         super(str);
         this.str = str;
         this.o = o;
      }
     public synchronized boolean isOk() {
         ok = true;
         o.notifyAll();
         return ok;
     }
     public void run() {
         for (i = 0; i < 10; i++) {
          synchronized (o){
             if (str == "B"){
              while (!ok) {
                 try {
                    System.out.println(str + " sta dormendo... ");
                     o.wait();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                  }
              } //while
              if (i == 0){
                 System.out.println(str + " si è svegliato!");
              }
             } //if B
            if (i == 5) {
              isOk();
              System.out.println(str + ": " + ok);
            }
           }//synch
           System.out.println(i + " " + getName());
           long s = (long)(Math.random()* 1000);
           try {
                 sleep(s);
           } catch (InterruptedException e) {}
          } //for
         System.out.println("FATTO! " + getName());
      }
     }
[/CODICE]
Grazie ancora di tutto e spero che possa essere utile anche agli altri!


> disposta ad aiutare qui...  si vede che sono in vacanza!
N> L'ho pensato anch'io...il periodo non invoglia certo a stare
N> davanti al PC collegato al NG!!
Avrei dovuto portarmi dietro anche il computer, con quello di mia
moglie mi trovo malino :).
N> che: 1- deve esistere un OGGETTO (magari creato ad hoc) che deve
N> gestire dall'alto la sincr di tutti i threads, dunque va passato a
N> loro come parametro
Non deve necessariamente essere  passato come parametro, deve comunque
essere raggiungibile da  tutti i thread. Ad esempio,  se i thread sono
tutti dello stesso  tipo (classe) allora l'oggetto (che  se non erro č
quello che in  "gergo" si chiama semaforo) puň  essere anche un membro
statico della classe.
N> if (str == "B")
Ladies and Gentlemen!  Welcome to  the "Questo codice funziona a botta
di fortuna" show!!!!
Scusa la  cattiveria e prendila  come uno scherzo. Sei  principiante e
questo errore l'ho visto fare a professionisti(*).
Il pezzo di codice che hai  fatto contiene un errore che per "botta di
fortuna" (piů  pratico e  sintetico di 'situazione  contingente estre-
mamente favorevole') non si manifesta.
Dove č l'errore ?
Nell'uso dell'operatore ==  per verificare l'uguaglianza del contenuto
di due stringhe.  Riccardo, se noti. ha usato equals().
Infatti  equals()  ritorna  true  se  due  stringhe  hanno  lo  stesso
contenuto. E == che fa ?
L'operatore == con  gli oggetti ritorna true se e  solo se l'oggetto č
il medesimo.  Ora -la botta di fortuna- tu fai sempre riferimento alla
stringa costante "B" che viene mappata sempre sul medesimo oggetto in
memoria, quindi == funziona come tu ti attendi.
Anche quando tu chiami il costruttore 
    public SimpleThread(String str, Object o) {
        super(str);
        this.str = str;
        this.o = o;
     }
lo fai con
        new SimpleThread("B", o).start();
ed al momento del caricamento  la costante String "B" delle due classi
(nell'invocazione  del costruttore  in ThreeThreadsTest  e  nel metodo
run() di SimpleThread) viene mappata sul medesimo oggetto in memoria.
Il costruttore riceve quindi il riferimento all'oggetto in questione e
quindi quando viene eseguita l'istruzione 
       if (str == "B")
a destra ed a  sinistra di == c'č sempre lo stesso  oggetto ed il test
passa come ti attenderesti.
Prova con questa versione del costruttore:
    public SimpleThread(String str, Object o) {
        super(str);
        this.str = str = "";
        this.o = o;
     }
e dimmi che succede :>.
(*) Qualche anno fa, lavorando su un progetto...
    (L č una collega in gamba, G sono io)
G (che ha trovato del codice simile al tuo) =
  "Ma [lista di moccoli & imprecazioni] ma non č possibile che
   scrivano del codice del genere!  Ma lo conoscono Java ?? -non sono
   principianti, sono pagati per programmare- QUESTO CODICE FUNZIONA A
   BOTTA DI CULO!!!"
L (che un po' ne ha le tasche piene dei miei mugugni) =
  "Gian, pianatala di reagire in questo modo, insomma non č il caso di
   parlare cosě del lavoro dei colle..."
G (con aumento del volume)
  "Un corno, codice del genere non dovremmo vederne!"
L (con aumento del volume per reazione)
  "Ma devi piantarla con questo atteggiamento"
G (volume++)
  "Diamine qui il codice funziona perché per puro culo gli arriva come
   parametro  direttamente  l'oggetto  String  literal, ma  appena  la
   stringa che arriva  ha subito un paio di  elaborazioni non funziona
   piů un [anatomia maschile]"
Ci ancora  un paio di  battute in tono  molto acceso e  la discussione
termina. Sono  convintissimo che L  sia incavolata, solo che  dopo non
molto tempo la sento rispondere al telefono
L (al telefono)
  "E mi  raccomando NON USARE  == per verificare l'uguaglianza  di due
   stringhe  come vedi  nell'esempio che  se  no, come  dice Gian,  il
   codice funziona a Botta di Culo..."
-- 
 /\           ___
/___/\_|_|\_|__|___Gian Uberto Lauri_____
  //--\| | \|  |   Integralista GNUslamico
\/                 e coltivatore diretto di Software
A Cesare avrei detto di scrivermi a	 | To Caesar I woul have said "emai me to
fn...@rat.vg
  (Lui cifrava in rot13) | 
fn...@rat.vg
  (He encrypted wit Rot13)

    codice funziona a Botta di Culo..."
Dovresti farne un trademark:
"Botta di Culo"(TM)
solo nei peggior source code di Caracas. :)
Ciao Jack

 statico della classe.
Giusto, il ragionamento non fa una piega...altrimenti che l'avrebbero 
inventato a fare il modificatore "static"??

Faccio ammenda, non si finisce mai d'imparare...d'altronde i NG esistono 
(grazie al cielo) proprio per questo.
E visto che siamo in argomento, č per BdC(TM)che mi č venuto in mente di 
postare lo stesso il mio pezzo di codice...in fin dei conti una volta 
risolto il problema potevo anche dire grazie e basta, ed ora saprei una 
cosa in meno!
Ringrazio infinitamente TUTTI coloro che mi hanno aiutato a chiarire le 
idee.

Ammettilo, siete un po' esauriti, no? :)
Dai che siamo ad agosto.....

> questo errore l'ho visto fare a professionisti(*).
N> Faccio ammenda, non si finisce mai d'imparare...
Come  ti ho  detto,  prendila come  uno  scherzo. Hai  detto che  stai
imparando ed  è un'errore in  cui è comprensibile che  un principiante
cada. 
N> esistono (grazie al cielo) proprio per questo.  E visto che siamo
N> in argomento, è per BdC(TM)che mi è venuto in mente di postare lo
N> stesso il mio pezzo di codice...
Peraltro, può essere utile basarsi sulla == per questioni di prestazioni.
Ma in  questo caso conviene usare  oggetti il cui  compito specifico è
proprio quello.
Ad esempio 
public class Colore {
   public final static ROSSO = new Colore();
   public final static VERDE = new Colore();
   public final static VIOLA = new Colore();
   private Colore() {}
}
definisce un  tipo (Colore) che  può assumere solo tre  valori, ROSSO,
VERDE  e VIOLA  -che guardacaso  sono gli  unici tre  oggetti  di tipo
Colore creati  e creabili-,  ed è perfettamente  lecito fare  cose del
tipo
Colore c1;
Colore c2;
//....
if (c1 == c2) {
// ...
}
basta essere consci di quello che si fa.
N> Ammettilo, siete un po' esauriti, no? :)
N> Dai che siamo ad agosto.....
No, ne ho un po' le palle piene di vedere codice cattivo...


 
Intanto GRAZIE per questo esempio, non sapevo si potesse fare in questo 
modo...è molto elegante!
A proposito di essere consci di quello che si fa, ne APPROFITTO e 
riprendo una cosa che hai postato in precedenza:
 >Non deve necessariamente essere  passato come parametro, deve comunque
 >essere raggiungibile da  tutti i thread. Ad esempio,  se i thread sono
 >tutti dello stesso  tipo (classe) allora l'oggetto (che  se non erro è
 >quello che in  "gergo" si chiama semaforo) può  essere anche un membro
 >statico della classe.
Domanda: potrebbe essere questa una pratica, come dire, un po' 
"pericolosa"? Nel senso che in qualche condizione il thread desiderato 
può non essere il proprietario del lock e lanciare quindi un'eccezione?
Lo chiedo perché ho provato a fare quello che dici sul mio esempio, 
giusto per approfondire, e se non ho interpretato male le righe che hai 
scritto, il codice che usa un ogetto statico interno alla classe 
dovrebbe essere questo:
[CODICE]
public class ThreeThreadsTest {
     public static void main (String[] args) {
         Object o = new Object();
         new SimpleThread2("A").start();
         new SimpleThread2("B").start();
         new SimpleThread2("C").start();
     }
}
public class SimpleThread2 extends Thread {
     public String str;
     public static boolean ok;
     int i;
     public static Object o;
     public SimpleThread2(String str) {
         super(str);
         this.str = str;
         o = new Object();
    }
     public synchronized boolean isOk() {
         ok = true;
         o.notifyAll();
         return ok;
     }
     public void run() {
         for (i = 0; i < 10; i++) {
          synchronized (o){
             if (str.equals("B")){
                 while (!ok) {
                 try {
                    System.out.println(str + " sta dormendo... ");
                     o.wait();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
              } //while
              if (i == 0){
                 System.out.println(str + " si è svegliato!");
              }
             } //if B
           if (i == 5) {
              isOk();
              System.out.println(str + ": " + ok);
           }
          }//synch
          System.out.println(i + " " + getName());
          long s = (long)(Math.random()* 1000);
          try {
                 sleep(s);
          } catch (InterruptedException e) {}
          }
         System.out.println( getName() + " ha finito!");
      }
     }
[/CODICE]
Ora, se l'oggetto "o" invece di public lo dichiarassi private:
private static Object o;
accade che se lo mando in esecuzione (in NetBeans) diverse volte di 
seguito, in qualche occasione ottengo:
Exception in thread "B" java.lang.IllegalMonitorStateException: current 
thread not owner
         at java.lang.Object.wait(Native Method)
ma in questo caso, il modificatore private o public perché dovrebbe 
cambiare il comportamento di tutto? In fin dei conti qui abbiamo istanze 
diverse della STESSA classe quindi una variabile (o ogetto che sia) 
private static dovrebbe essere cmq diponibile a tutte le istanze della 
classe, o no???
Ovviamente, nel caso in cui l'oggetto venga passato come parametro 
dall'esterno il modificatore è per forza public quindi il problema non 
si pone.
Scusate l'ignoranza, portate pazienza....

statico della classe.
Domanda: potrebbe essere questa una pratica, come dire, un po'
"pericolosa"? Nel senso che in qualche condizione il thread desiderato
potrebbe non essere il proprietario del lock e lanciare quindi un'eccezione?
Lo chiedo perché ho provato a fare quello che dici sul mio esempio,
giusto per approfondire, e se non ho interpretato male le righe che hai
scritto, il codice che usa un ogetto statico interno alla classe
dovrebbe essere questo:
[CODICE]
public class ThreeThreadsTest {
     public static void main (String[] args) {
         Object o = new Object();
         new SimpleThread2("A").start();
         new SimpleThread2("B").start();
         new SimpleThread2("C").start();
     }
}
public class SimpleThread2 extends Thread {
     public String str;
     public static boolean ok;
     int i;
     public static Object o;
     public SimpleThread2(String str) {
         super(str);
         this.str = str;
         o = new Object();
    }
     public synchronized boolean isOk() {
         ok = true;
         o.notifyAll();
         return ok;
     }
     public void run() {
         for (i = 0; i < 10; i++) {
          synchronized (o){
             if (str.equals("B")){
                 while (!ok) {
                 try {
                    System.out.println(str + " sta dormendo... ");
                     o.wait();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
              } //while
              if (i == 0){
                 System.out.println(str + " si è svegliato!");
              }
             } //if B
           if (i == 5) {
              isOk();
              System.out.println(str + ": " + ok);
           }
          }//synch
          System.out.println(i + " " + getName());
          long s = (long)(Math.random()* 1000);
          try {
                 sleep(s);
          } catch (InterruptedException e) {}
          }


> anche un membro statico della classe.
Perdonami se non ho letto subito bene tutto il tuo codice.
Allora, penso che il problema  derivi dal fatto che non ti sincronizzi
sempre sul semaforo.
Nel metodo run  infatti ti sincronizzi su o,  mentre isOk è dichiarato
sincronizzato  a livello  di  metodo. Quindi  va  a fare  il lock  sul
"this". A  naso, dato che o  è referenziato da tutti  i thread, quando
c'è il lock  su uno di loro  la cosa crea problemi nel  momento in cui
fai la wait() su o.
Non  è  che  ti capita  anche  di  avere  problemi (saltuari)  con  la
notifyAll() ?


 
Non ho capito bene cosa intendi con problemi saltuari sulla notifyAll();
se ti riferisci allo stato della variabile boolean, sino ad ora è sempre 
stato rilevato il suo cambiamento di valore; quello che posso dirti è 
che l'unica eccezione (o problema) emerso sin'ora nell'esecuzione 
ripetuta del codice riguarda solo la proprietà del monitor e solo nel 
caso in cui "o" sia dichiarato dentro alla classe thread; dichiarandolo 
invece nella classe main l'eccezione non è ancora avvenuta.

> 
N> Non ho capito bene cosa intendi con problemi saltuari sulla
N> notifyAll();
Hai detto che ogni tanto ti va in exception sulla wait().
Sulla notifyAll() ha mai dato problemi ?


 


>>>> "N" == Nad  <""nadrm\"@NO...@Myahoo.com"> writes:
Prova ad usare questa versione di run()
    public void run() {
        for (i = 0; i < 10; i++) {
			if (str.equals("B")){
				while (!ok) {
					System.out.println(str + " sta dormendo... ");
                    // Mi sincronizzo solo per la wait
					synchronized (o){
						try {
							o.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
 
				} //while
				if (i == 0){
					System.out.println(str + " si č svegliato!");
				}
			} //if
			if (i == 5) {
				if (!ok) {
					isOk();
					System.out.println(str + ": " + ok);
				}
			}
			System.out.println(i + " " + getName());
			long s = (long)(Math.random()* 1000);
			try {
				sleep(s);
			} catch (InterruptedException e) {}
		}
        System.out.println("FATTO! " + getName());
	}
-- 


Gian Uberto Lauri ha scritto:

Hai ragione, cosě sembra funzionare perfettamente!  :)
Riflettendoci, il lock serve solo per il wait e solo per il thread "B" 
quindi inutile bloccare la risorsa quando non č necessario bloccarla: 
cosě si evita che quando serve non sia bloccata da qualcun'altro!
Grazie, č stato molto istruttivo!!!

Con i ringraziamenti mi ci aggiungo anche io perchè stavo meditando di
riprendermi in mano il TIJ 2nd Edition per rivedermi per benino thread
e amici vari e voi mi state dando dell'ottimo materiale su cui
ricominciare
Grazie  a tutti!!!
Marco

>>>> "N" == Nad  <""nadrm\"@NO...@Myahoo.com"> writes:
N> Hai ragione, così sembra funzionare perfettamente!  :)
Una curiosità, che JDK e s.o. ospite usi ?


 
Uso NetBeans con JRE 1.6.22.2, il SO è XP Sp2 che gira su VMWare. Questo 
mi ha permesso di portarmi lo stesso "ambiente di sviluppo" man mano che 
cambio PC, ecco perché te lo consigliavo.
Preciso che a me interessa principalmente sviluppare in J2ME, quindi uso 
NetBeans: ha un buon supporto per la MicroEdition di Java e per l'UML. 
Ho già fatto un programmino che legge i dati da un'antenna GPS BT, li 
memorizza in un file di testo e manda una mail; è già abbastanza robusto 
ma non ho sfruttato sino in fondo i threads, ecco perché m'interessa 
approfondire bene l'argomento. Ovviamente alcuni metodi sono assenti 
nella MicroEdition, ma guarda caso nell'esempio che abbiamo trattato ho 
volutamente fatto a meno di loro!
Qualcuno sul NG ha gradito l'approfondimento che è stato fatto, sono 
contento che sia stato utile anche ad altri!
Ti ringrazio ancora per tutto il supporto che mi hai dato...

> 
N> Uso NetBeans con JRE 1.6.22.2, il SO è XP Sp2 che gira su
N> VMWare. Questo mi ha permesso di portarmi lo stesso "ambiente di
N> sviluppo" man mano che cambio PC,
Preferisco la soluzione "ambiente di sviluppo facilmente replicabile".
Comunque la mia richiesta era mirata a sapere da un lato che versione
dell'ambiente e che sistema erano in uso.
Il sistema  operativo, in particolare,  può avere una  certa influenza
coi thread, in quanto non su  tutti i sistemi operativi è garantito il
comportamento preemptive dei thread.
E questo perché  tu hai detto di rilevare problemi  in questo pezzo di
codice
	synchronized (o){
		if (str.equals("B")){
			while (!ok) {
				try {
					System.out.println(str + " sta dormendo... ");
					o.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
per una IllegalMonitorStateException causata da o.wait().
Se fai  la wait() hai per  forza eseguito la  synchronized(o) e quindi
dovresti essere il proprietario del monitor...
Mi piacerebbe capire perché sul  tuo sistema i lock vengano rilasciati
tra  la synchronized(o)  e la  o.wait(), ma  qui mi  sa che,  almeno a
livello di Java, si tratti di un Eisenbug....
Per  la cronaca,  sotto GNU/Linux  2.6.18-4-686 #1  SMP,  java version
"1.4.2_08"  Java(TM) 2  Runtime Environment,  Standard  Edition (build
1.4.2_08-b03)  Java HotSpot(TM) Client  VM (build  1.4.2_08-b03, mixed
mode) non rilevo problemi di sorta...


Gian Uberto Lauri ha scritto:

Mi hai fatto venire un dubbio ed ho deciso di fare la prova alla vecchia 
maniera, cioè compilando ed eseguendo da riga di comando (nel mio caso 
DOS) per vedere se cambiava qualcosa. In questo caso la vers JRE è la 
1.5.0 (build 1.5.0_09-b03).
Dopo diverse esecuzioni di seguito è spuntato l'errore!
Ecco l'output:
E:\Documenti\SimpleThread>java ThreeThreadsTest
0 A
B sta dormendo...
0 C
Exception in thread "B" java.lang.IllegalMonitorStateException: current 
thread n
ot owner
         at java.lang.Object.wait(Native Method)
         at java.lang.Object.wait(Unknown Source)
         at SimpleThread.run(SimpleThread.java:34)
1 A
2 A
1 C
3 A
4 A
2 C
A: true
5 A
3 C
6 A
4 C
7 A
5 C
8 A
6 C
9 A
FATTO! A
Threads Attivi: 3
7 C
8 C
9 C
FATTO! C
Threads Attivi: 2
-----
Ho fatto un conteggio ed in 20 esecuzioni è apparso 3 volte, per quello 
che può valere una simile statistica.
Sapevo che la gestione dei thread era legata al SO, anche alla Pipeline 
(x le bolle) e x il fatto che non c'è proprio sequenzialità 
nell'esecuzione delle istruzioni. Ho letto (ma NON ho finito di 
leggerlo) un articolo interessante e molto tecnico qui: 
http://www.prato.linux.it/~fcasadei/cgi-bin/moin.cgi/JavaThreadTrabocchetti?action=AttachFile&do=get&target=FlavioThread.pdf
Certo è INQUIENTANTE il fatto che un codice possa comportarsi in maniera 
diversa da una macchina all'altra; più che altro, fintanto che il 
problema appare ci si può regolare di conseguenza, ma quando non appare....