Newsgroup: it.comp.java
Titolo: Applicare sincronizzazione dei thread

Sto cercando di comprenderne il meccanismo, e mi piacerebbe molto
poterla applicare all'esempio qui sotto proposto (ne uscirebbe un
eccellente caso didattico).
Qualcuno più esperto sarebbe così gentile da darmi una dritta? Non
riesco a raccapezzarmici .-)
grazie molte..
public class Bancomat extends Thread{
	static int soldiinbanca;
	boolean operazione;
	int quantità;
	public Bancomat(boolean tipo, int somma){
		operazione = tipo;
		quantità = somma;
	}
	synchronized void eseguiOperazione(){
		if (operazione == true){
			/* Rallentamento che produce (testato)
			l'inconveniente di sincronizzazione */
			//Thread.sleep(1000);
			soldiinbanca = soldiinbanca + quantità;
			System.out.println("Versamento di: " + quantità);
		}
		else{
			if (soldiinbanca - quantità > 0){
				soldiinbanca = soldiinbanca - quantità;
				System.out.println("Prelievo di: " + quantità);
			}
			else{
				System.out.println("operazione non eseguita!");
		}
		}
	}
	public void run() {
		eseguiOperazione();
		System.out.println("Stato del conto: " + soldiinbanca);
	}
	public static void main(String args[]) {
		soldiinbanca = 1000;
		System.out.println("Conto iniziale: " + soldiinbanca);
		Bancomat operazione1 = new Bancomat(true, 200);
		Bancomat operazione2 = new Bancomat(false, 500);
		Bancomat operazione3 = new Bancomat(false, 600);
		operazione1.start();
		operazione2.start();
		operazione3.start();
	}
}

 eccellente caso didattico).
[...]
La classe Bancomat rappresenta un'operazione di transazione
effettuabile da 3 "clienti-thread": per loro definizione, nel momento
in cui vengono lanciati non sappiamo chi andrà in esecuzione e quando.
Dunque si utilizza il blocco synchronzed per garantire che chiunque
entri a compiere le operazioni sulla variabile statica condivisa
soldiinbanca agisca da solo (mette un "lucchetto", effettua operazioni
e lo rilascia per il prossimo thread) evitando la cosiddetta "race
condition".
In particolare, dopo aver posto nel main() a 1000 la variabile
suddetta, si creano 3 thread e si mandano in esecuzione concorrente:
come specificato nel metodo run(), si esegue eseguiOperazione().
Quest'ultima esegue un versamento o un prelievo della quantità
specificata. Ad essere giustamente pignoli, interrogare la variabile
condivisa fuori dal blocco è fondamentalmente sbagliato!
Se il metodo eseguiOperazione non fosse "sincronizzato", ci sarebbe il
rischio che un thread venga estromesso dal ciclo di esecuzione prima di
aver finito, con conseguenze imprevedibili per lo stato finale della
variabile (anche se non è detto che tu riesca a vederlo dalla traccia
dell'esecuzione...).
PS:  ma come mai un sacco di gente si interessa ai thread senza sapere
nulla di multithreading? Didatticamente non è un grandissimo esempio,
la vera sincronizzazione richiederebbe l'uso di wait() e notify()... )

 dell'esecuzione...).
Ciò che hai scritto mi è abbastanza chiaro e corrisponde infatti al
ragionamento che ho seguito per preparare questo esempio. Grazie
comunque per la chiarezza di esposizione.
Il mio problema è appunto che 'eseguiOperazione' (così come l'ho
creata) non è per niente sincronizzata (decommentando lo 'sleep'
riesco infatti a tracciarne gli esiti imprevisti) e non so appunto come
riuscire sincronizzarla. Non riesco a applicare la sincronizzazione a
questo contesto. Potresti darmi una mano?
Grazie in anticipo..
Fabrizio

 la vera sincronizzazione richiederebbe l'uso di wait() e notify()... )
Perche' fa figo.

Jack ha scritto:

Scusate, qualcuno sarebbe così cortese da indicarmi come usare wait()
e notify() nell'esempio citato? Tutto mi sento meno che figo. Ho solo
bisogno di un esempio semplice con cui possa spiegarlo, e invece non mi
riesce.. Tutto qua..
Grazie ancora.

 e notify() nell'esempio citato? [...]
il fatto è che lì devi cambiare un po' di cose, l'esempio che proponi
è un po' confuso e andrebbe cambiato di molto. Dovresti (a quanto ne
so) implementare una politica (FIFO ad es.) visto che i monitor di Java
non ne prevedono una, ed è un po' complesso secondo me.
Piuttosto prova questo codice, che per capire wait() notify() forse va
bene:
public class GestoreFIFO{
private int turn=0; //turno "globale"
public synchronized void fai(int myTurn){
	while (turn!=myTurn)
		try{
			wait();
		}
	catch(InterruptedException e){};
	System.out.println("Passa "+myTurn);
	turn++;
	notifyAll();
}
public static void main(String[] args){
	GestoreFIFO gf=new GestoreFIFO();
	for (int i=0;i<1000;i++)
		new MyThread(gf,i).start();
}
}
class MyThread extends Thread{
private GestoreFIFO gf;
private int myTurn;
public MyThread(GestoreFIFO gf, int myTurn){
 
this.gf
=gf;
	this.myTurn=myTurn;
}
public void run(){
	gf.fai(myTurn);
}}
Output:
Passa 0
Passa 1
...
Passa 9
Spero che sia chiaro...
NB non avevo intenti polemici, semplicemente mi incuriosisce che tanta
gente si interessi proprio di thread, che sono una cosa abbastanza
complessa e molto specifica... difficile capire tutto senza aver letto
qualcosa a riguardo... tutto qui

 bene:
Grazie mille!
Con queste piccole modifiche riesco a spiegare e dimostrare
egregiamente il problema riguardo la mancata sincronizzazione dei
thread (tracciandone gli inconvenienti), e ad illustrarne la possibile
soluzione usando wait() e notify().
ciao..
public class riprova{
private int turn=0; //turno "globale"
public void fai(int myTurn){
//while (turn!=myTurn)
//try{
//wait();
//}
//catch(InterruptedException e){};
if (myTurn % 2 == 0) {
rallenta();
System.out.println("Paro "+myTurn);
}
if (myTurn % 2 != 0) {
System.out.println("Disparo "+myTurn);
}
turn++;
 //notifyAll();
}
void rallenta(){
try {
Thread.sleep(1000);	// 1????
}
catch (InterruptedException e) {
System.out.println(e);
}
}
public static void main(String[] args){
riprova gf=new riprova();
for (int i=0;i<100;i++)
new MyThread(gf,i).start();
}
}
class MyThread extends Thread{
private riprova gf;
private int myTurn;
public MyThread(riprova gf, int myTurn){


 }
Thread.sleep(x) effettua una pausa di x millisecondi...