Newsgroup: it.comp.java
Titolo: Arbitraggio nell'avvio dei thread

Sto collaudando le caratteristiche multi threading di Java e mi trovo
di fronte ad un comportamento bizzarro che non riesco a capire:
Il main chiama in sequenza 4 metodi:
1) stampaTutto
2) set
3) reset
4) stampaTutto
ma dai messaggi di traccia si vede che i metodi vengono eseguiti in
questo ordine:
1) stampaTutto
2) stampaTutto
3) reset
4) set
e la cosa e' per me assurda perche' non viene rispettato l'ordine di
esecuzione da me impostato.
Preciso che i metodi set e reset agiscono da altrettanti thread che
sono indipendenti dal main, mentre stampaTutto dipende dal thread del
main.
Tutti i metodi invocati sono pero' sincronizzati e quindi la loro
esecuzione non puo' sovrapporsi, cosa che in effetti correttamente
accade.
La stranezza e' proprio nell'ordine con cui i metodi vengono attivati:
non capisco per quale ragione questa viene cambiata.
A seguire il sorgente incriminato:
========================================
public class Gestore{
    private Risorsa r;
    private Set set;
    private Reset reset;
    public Gestore(){
        r= new Risorsa();
        set= new Set( r );
        reset= new Reset( r );
    }
    public void esperimento(){
        r.stampaTutto();
        set.start();
        reset.start();
        r.stampaTutto();
    }
    public static void main( String args[] ){
    	Gestore g= new Gestore();
    	g.esperimento();
    }
}
=====================================
public class Set extends Thread{
    private Risorsa r;
    public void run(){
            r.set( 5000 );
    }
    public Set( Risorsa r ){
        this.r= r;
    }
}
=================================
public class Reset extends Thread{
    private Risorsa r;
    public void run(){
            r.reset( 3000 );
    }
    public Reset( Risorsa r ){
        this.r= r;
    }
}
=================================
public class Risorsa{
    private int a, b, c;
    public Risorsa(){
        a= 0;
        b= 0;
        c= 0;
    }
    public void attendi( long ms ){
        try{
            Thread.sleep( ms );
        } catch( InterruptedException e ){
            System.out.println( e );
        }
    }
    public synchronized void set( long attesa ){
        System.out.println( "inizio metodo set" );
        a= 1;
        attendi( attesa );
        b= 1;
        attendi( attesa );
        c= 1;
        attendi( attesa );
        System.out.println( "fine metodo set" );
    }
    public synchronized void reset( long attesa ){
        System.out.println( "inizio metodo reset" );
        a= 0;
        attendi( attesa );
        b= 0;
        attendi( attesa );
        c= 0;
        attendi( attesa );
        System.out.println( "fine metodo reset" );
    }
    public synchronized void stampaTutto(){
        System.out.println( "inizio metodo stampa" );
        System.out.println( "a=" + a + " b=" + b + " c=" + c );
        System.out.println( "fine metodo stampa" );
    }
}

non capisco per quale ragione questa viene cambiata.
E' corretto. I thred sono ASINCRONI.  Cio' significa che ognuno va per i fatti
propri alla velocita' propria. Cio' li rende completamente imprevedibili.
Per fare un esempio un po' poco tecnico immaginali come una gara di persone
fra cui uno scattista, uno con una gamba ferita, uno che si ferma al bar a
bere un caffe eccetera.
Se vuoi che rispettino un ordine devi SINCRONIZARLI esattamente come si si
sincronizzano 4 staffettisti. 
Il metodo SYNCHRONIZE garantisce la mutua esclusione dei metodi e quindi
blocca tutti  gli altri metodi quando uno e' in esecuzione. Poi pero' non dice
nulla su chi svegliera' per primo, quindi consideralo random.
Se vuoi sincronizzare e avere il controllo devi usare le istuzioni
wait/notify
Ciao NIcola
-- 


Grazie per la risposta.
Quindi se voglio avviare N thread in un ordine da me stabilito non
basta scrivere le N start() in quell'ordine?
Quello che dici a proposito della asincronicita' e' logico ma ancora
non riesco a capire bene: l'istruzione start() del primo thread viene
eseguita prima dell'istruzione start() dell'ultimo thread.
Perche' allora l'ultimo thread parte per primo, ed il primo per
ultimo?
Per quanto riguarda il meccanismo wait-notify: l'ho ben compreso. Ma
speravo che per il semplice scopo che mi prefiguravo (far partire dei
thread in un certo ordine) potessi farne a meno.


Ho scoperto un'altra cosa in proposito: se il main che lancia i 4
metodi su descritti viene sostituito da un altro thread ecco che
l'ordine di avvio viene rispettato correttamente. E' un caso o c'e'
una logica dietro?

 main.
vabbeh e che ti sorprendi? se i metodi vengono eseguiti in thread 
separati senza occuparsi della sincronizzazione è ovvio che non sono 
sequenziali. per prima cosa devi chiederti perché hai strutturato in 
multithreading (ci sarà un perché) e una volta trovata la risposta 
studiarsi la parte che riguarda la sincronizzazione dei thread (wait(), 
notify() e notifyAll()) per far si che le cose avvengano nell'ordine 
prefissato.

 sequenziali.
Si da' il caso pero' che questi thread separati, per funzionare,
abbiano bisogno tutti della stessa risorsa. Su questa risorsa c'e' un
lock (synchronized). Quindi i thread devono PER FORZA essere svolti in
modo sequenziale: prima uno, poi l'altro, eccetera.
E in effetti VENGONO SVOLTI in modo sequenziale, come previsto.
Il mio problema e' un altro.
Io lancio il primo thread che locka la risorsa, svolge quello che deve
fare e poi termina, liberando la risorsa.
Successivamente lancio il secondo thread che per fare quello che deve
fare ha bisogno anch'esso della risorsa. Senonche', trovandola
bloccata dal primo thread (che e' ancora in esecuzione), si mette in
attesa che questa si liberi. La risorsa si libera quando il primo
thread termina. Solo allora il secondo thread puo' partire.
Ecco allora che ho sincronizzato i due thread: il secondo parte solo
quando il primo ha finito, pur avendoli avviati quasi assieme.
Il fatto e' che non succede quanto da me descritto.
Infatti il secondo thread, pur essendo stato lanciato per secondo,
viene eseguito per primo.
Ed e' questo che non capisco. Non credo c'entri niente la wait() e la
notify(), qui c'e' in ballo il meccanismo di funzionamento della
synchronized.
Anzi di piu': sembra che java analizzi una sequenza di istruzioni e
decida di eseguirle non in sequenza. Oppure decida di tenere il primo
thread un po' in pausa (per un qualche motivo) e nel frattempo il
secondo thread puo' scattare. Quando quella pausa finisce ormai e'
troppo tardi ed il primo thread deve aspettare il suo turno.
Queste sono le spiegazioni che mi sono venute in mente. Il progetto
che ho postato non ha alcuna utilita'. Mi serve solo per collaudare
bene le potenzialita' multi-threading di Java per pensare magari di
integrarle in qualche futuro progetto.
Spero di aver chiarito meglio la situazione. Se c'e' qualcuno esperto
di multi-threading potrebbe aiutarmi a capire.
Grazie
Lu

di multi-threading potrebbe aiutarmi a capire.
E' il tuo approccio che e' sbagliato. Tu hai scritto un programma errato e in
base al funzionamento di quello vuoi capire la programmazione concorrente.
Auguri, impiegari piu' tempo e forse non la capirai mai.
La sincronizzazione in java si fa con wait/signal 
Il tuo programma NON E' SINCRONIZZATO e i 3d potranno arrivare in ordine
qualasiasi se sposti il programma su un altro sistema operativo, su una
macchina con carico di verso, su una jvm devrsa eccetera, o cambia il tempo.
Il sistema operativo fa quello che vuole, puo' dare priorita' ai thread,
dargli o meno risorse, cpu, memoria, svegliarli o non svegliarli.
Inoltre le operazioni di IO sono tipicamente cariabili, hai mai guardato i
tempi di esecuzione di una query o scrittura su file ? sono casuali , lo
stesso e' per la memoria e tutto il resto, solttanto che ce ne si accorge di
meno.
Come hai notato tu stesso e' bastato alterare minimamente il programma che
l'ordine e' cambiato.
-- 

A questo punto non mi resta che usare la join() per sincronizzare i
processi.
Preferisco utilizzare la wait per elaborazioni piu' sofisticate.
Grazie per la risposta


Scusate ma in tutto questo casino non ho capito che utilità ha creare 
dei thread per eseguire un'elaborazione sequenziale... c'è qualcosa che 
ci sfugge?
-- 
Dimitri De Franciscis
Phone:  
(+39) 3401570778
Website: 
http://www.megadix.it/

 ci sfugge?
Si'.
In generale, anche se ci sono dei thread che agiscono in parallelo,
puo' essere necessario sequenzializzare certe operazioni, in modo da
non creare sovrapposizioni e, in ultima istanza, inconsistenze.
Per questo e' stato creato il costrutto synchronized: impedendo di
fatto (e in certi casi) che piu' thread vengano comunque eseguiti in
parallelo si viene a creare una sorta di sincronizzazione (da cui il
nome).
Il progetto da me postato serviva a mettere sul "banco di prova"
questa caratteristica e ovviamente, preso da solo non ha molto senso
come applicazione multi-threaded. Un po' come provare un motore sul
banco di prova, appunto. A che serve, se non va da nessuna parte?
Serve, perche' cosi' si testano le caratteristiche di quel motore.


Forse sei convinto che usare synchronized crei una specie di coda di 
attesa, ma non � affatto cos�! Come ti hanno gi� detto il S.O. fa quel 
cacchio che gli pare: round-robin, fcfs, random... tutto � lecito!
Prova a dare un'occhiata al package java.util.concurrent, in particolare 
la classe CyclicBarrier serve per far aspettare tutti i thread ad un 
punto preciso.



Ho visto, e la cosa mi sembra molto interessante, anche se prima di
utilizzare altre classi vorrei capire bene come si comporta la famosa
classe Thread.
Ti ringrazio della risposta
Lu

eseguito le istruzioni A e B?
Allora ci riproviamo :-)
1. STart
t.start  dice al SISTEMA OPERATIVO che un thread richiede di essere lanciato
sulla macchina
2. palla al SO
Il sistema operativo prende il 3d e lo mette in una SUA lista insieme a TUTTI
gli altri 3d della macchina
3. Il parallelismo
A questo punto sempre il sistema operativo da al 3d una priorita' e inizia a
farlo andare in parallelo a tutti gli altri 3d della macchina, non solo del
tuo programma, 3d di sistema, di altri programmi eccetera ( se apri il gestore
risorse di windows ti dice quanti sono )
Esistono varie politiche per alternare i 3d nelle varie cpu/memorie/IO, round
robin, a priorita', a chi paga di piu ( succedeva negli anni 70 quando una
macchina veniva usata da una intera regione :-) ) eccetera
Quindi rasserenati e non pensarci piu' a chi vince fra i tuoi thread.
ciao Nicola
-- 


Quindi il thread principale eseguira', in sequenza, le istruzioni A e
le istruzioni B.
Dopodiche' i thread t1, t2, e t3 entreranno in questa struttura (che
non sara' una coda a questo punto) dove con una tempistica
imprevedibile saranno avviati, in qualunque ordine.
E' cosi'?

E' cosi'?
ohhhhhhhhhh
iniziamo a ragionare. NON C'E' regola su chi parte e chi arriva prima nel
mondo asincrono.
Sta a noi sincronzzare.
A occhio comuqnue il tuo programma iniziale ha degli "errori filosofici" di
fondo. Mi pare che "dai in giro" la classe risorsa che e' sincronizzata e
quindi e' lei che crea le code ( sbagliate )
P.S. ti prego non coinvolgermi perche' so che e' divertente, l'ultima volta
uno studente mi ha chiesto un'opinione sul suo micro-progettino per l'esame,
alla fine sono stato una settimana piena con lui a farlo ( in pratica
marinando il lavoro :)))) ) 
e sai quanto ha preso all'esame ?
18 :-((((((( ( comquneu lui era contento, perche' e' un esame tosto )
E non mi ha mai detto gli errori che avevamo fatto perche' il prof non li
diceva, anche se mi pareva strano.
Comuqnue sappi che la concorrenza e' probabilmente la parte logicamente piu'
impegnativa della programmazione, anche molto affascinante.
il fatto e' che noi "comuni mortali" programmiamo 99% non concorrente e quindi
non siamo allenati, se beccassimo un developer concorrente "vero" ovviamente
risponderebbe con + disinvoltura a ogni questione.
ciao Nicola
-- 

 impegnativa della programmazione, anche molto affascinante.
Immagino... anche se grossi progetti concorrenti non ne ho ancora
visti, per potermi fare un'idea delle potenzialita'. Ad occhio mi
vengono subito in mente i video game (con i personaggini che sono
thread) ma ancora non riesco ad immaginare un gestionale multi-
threaded. Forse si potrebbe far svolgere ad un thread una query mentre
l'utente puo' continuare a gironzolare con le restanti funzioni
dell'applicativo...?
grazie
Lu