Ok, allora ecco svelato il codice!
Innanzitutto la seguente è la versione
senza wait/notify. Ho solo preso il tuo codice nel post iniziale, l'ho scritto/pulito meglio ed ho aggiunto uno sleep di durata "casuale" prima del call().
public class Synch {
public static void main(String[] args) {
CallMe target = new CallMe();
new Caller(target, "Hello");
new Caller(target, "Synchronized");
new Caller(target, "World");
}
}
class Caller implements Runnable {
private CallMe target;
private String msg;
public Caller(CallMe target, String msg) {
this.target = target;
this.msg = msg;
new Thread(this).start();
}
public void run() {
try {
Thread.sleep((long) (40 * Math.random()));
} catch (Exception e) {}
target.call(msg);
}
}
class CallMe {
public synchronized void call(String msg) {
System.out.print("[" + msg);
try {
Thread.sleep(1000);
} catch (Exception e) {}
System.out.println("]");
}
}
Ho aggiunto lo sleep casuale perché senza di questo, e dipendentemente dal S.O./piattaforma e dalle logiche di scheduling dei thread, è possibile (e nemmeno troppo difficile) che l'output Hello Synchronized World sia "quasi" sempre questo.
Con lo sleep casuale invece è altamente più probabile che l'output vari anche da una esecuzione all'altra. Prova ad avviarlo un po' di volte (es. una decina) e lo vedrai tu stesso.
Ora aggiungo al codice sopra solo alcune cose:
- un "index" in Caller (quindi variabile di istanza, modifica al costruttore, ecc..)
- un "currentIndex" nel CallMe con i wait/notifyAll nel call()
public class Synch {
public static void main(String[] args) {
CallMe target = new CallMe();
new Caller(target, 0, "Hello");
new Caller(target, 1, "Synchronized");
new Caller(target, 2, "World");
}
}
class Caller implements Runnable {
private CallMe target;
private int index;
private String msg;
public Caller(CallMe target, int index, String msg) {
this.target = target;
this.index = index;
this.msg = msg;
new Thread(this).start();
}
public void run() {
try {
Thread.sleep((long) (40 * Math.random()));
} catch (Exception e) {}
target.call(index, msg);
}
}
class CallMe {
private int currentIndex = 0;
public synchronized void call(int index, String msg) {
try {
while (index != currentIndex) {
wait();
}
} catch (InterruptedException e) {
System.err.println(e);
return;
}
System.out.print("[" + msg);
try {
Thread.sleep(1000);
} catch (Exception e) {}
System.out.println("]");
currentIndex++;
notifyAll();
}
}
Ora l'output è sempre quello dettato dalla sequenza 0 1 2. Puoi anche provare a costruire i Caller passando i numeri con un'altra sequenza e vedrai che l'output seguirà quella!
Il catch di InterruptedException è solo un pro-forma che nel caso della applicazione non dovrebbe mai capitare.
Se non è chiaro ... spiego!