View Full Version : [java]domanda thread e parametri
nuovoUtente86
14-10-2007, 11:26
Googlando alla ricerca di materiale sui thread mi sono imbattuto in una discussione di un forum, che ogni tanto googlando spunta fuori e presumo sia abbastanza importante,che mi ha fatto rimanere perplesso.
http://www.p2pforum.it/forum/showthread.php?t=153714
Si parla del passaggio dei parametri al thread e qualcun sostiene che avviene per copia..Ora è vero che java copia il valore per i primitivi e una copia del reference per le classi ma nel caso in discussione si parlava di un vettore di byte per cui pur andando a passare la copia del reference dello stesso le eventuali operazioni del tipo byte[]vettore=valore; dovrebbe avere ripercussioni sulla struttura e non scivolare via come dalla discussione appare.Purtroppo manca il codice dell' applicazione che renderebbe migliore l' idea.
P.S posto qui perchè non mi andava di fare una registrazione in quel forum,per una sola discussione...trovandomi qui molto bene.
Cosa ne pensate dell' argomento?
per quanto ne so il passaggio di oggetti (un array è un oggeto) avviene SOLO E IN OGNI CASO per reference. quindi è escluso che solo nel caso di più thread questo non accada. il problema credo che consista nel fatto che per ragioni di efficienza, il thread si fa delle copie locali di campi e dati, e quindi anche se non dovrebbe essere così hai due copie dello stesso array (bah, sapevo che lo si faceva solo con tipi primitivi, bah ripeto). prova a dichiarare l'array transient, che dovrebbe impedire questa ottimizzazione.
nuovoUtente86
15-10-2007, 14:02
Transient nel caso non è inerente perchè in pratica indica che in fase di serializzazione lo stato non va salvato.
Non ho ben capito cosa vuoi dire con il discorso variabili ma in pratica java fa questo discorso(che poi si diceva anche nel post linkato):i tipi primitivi(int,boolean.....) vanno per valore,gli oggetti per reference o meglio copiando l' indirizzo dell' oggetto.In sostanza passando un oggetto viene creato un riferimento locale in piu alla variabile.Ora si hanno 2 effetti:se chi lavora sul reference copiato effettua un assegnamento,sull' oggetto originale non accade nulla,se effettua una chiamata a metodo settatore cambia anche lo stato dell' oggetto originale.Nel caso di array fare array[i]=new Object(); equivale a fare oggetto.set().
volevo dire volatile. mi confondo, non ho ancora avuto modo di usarli.
volevo dire volatile. mi confondo, non ho ancora avuto modo di usarli.
Editato
Non ho capito esattamente il tema dell'altra discussione. Comunque la frase "il passaggio dei parametri ai Thread avviene per copia" ha un suo significato.
Secondo le specifiche del linguaggio ogni Thread ha una propria memoria di lavoro (o, meglio, può agire come se avesse una propria memoria di lavoro).
I valori condivisi (cioè le quantità numeriche per le variabili di tipo primitivo primitive o gli indirizzi per le variabili di tipo riferimento) sono copiati nella memoria di lavoro dei Thread: ogni Thread ha (può avere) la sua copia.
I valori condivisi sono quelli residenti nell'heap, vale a dire i campi. Dunque ogni Thread possiede una copia locale dei campi su cui lavora.
La parola chiave "volatile" apposta ad un campo impone al Thread di riversare nell'heap ogni scrittura su quel campo. Senza la parola chiave volatile e senza gli altri meccanismi di sincronizzazione ogni scrittura operata da un Thread ha un effetto limitato alla copia del campo che il Thread ha creato per sè. Lo stesso vale per le letture: se normalmente la lettura di un campo per un Thread corrisponde alla lettura della copia "locale" (al Thread) di quel campo, volatile costringe il Thread a prelevare il valore dall'heap, aggiornando di fatto il valore della copia locale a quello attualmente presente nella memoria condivisa.
Non conosco altre questioni relative alla copia-non copia che riguardino i Thread. Onde evitare antichi fraintendimenti mai del tutto sopiti vorrei ribadire che in Java il passaggio dei valori è sempre ed esclusivamente per valore (altresì detto per copia) a prescindere dal fatto che la variabile considerata sia di tipo primitivo (il cui valore corrisponde alla quantità numerica rappresentata dalla variabile) o di tipo reference (il cui valore corrisponde all'indirizzo di un oggetto).
nuovoUtente86
18-10-2007, 15:11
Se però analizziamo quello che succede con questo piccolo pezo di codice,c'è qualcosa che cozza con la tua ottima spiegazione.Cerchiamo di capire il perchè.
public class ProvaThread extends Thread{
private int[] array;
private int numero;
public ProvaThread(int[]a,int n){array=a;numero=n;}
public void run(){
array[numero]=numero;}}
public class Test{
public static void main(String[]args){
int[] array={0,0,0,0,0};
new ProvaThread(array,1).start();
new ProvaThread(array,2).start();
for(int i:array)System.out.println(i);
}}
Ora nel main avremmo 3 thread :il main stesso e i 2 ProvaThread.A meno della schedulazione avremmo in output
0,1,2,0,0 ovvero ognuno dei 2 Thread scrive nella posizione n ricevuta in ingresso dell' array ricevuto in ingresso.Se ogni thread agisse sulla sua copia privata avremmo in output 0,0,0,0,0,0...
Nulla cozza: in quello che ho scritto occorre tenere conto dei "può". Quei "può" indicano che quanto scritto nelle specifiche del linguaggio Java (The Java Language Specifications, 3th ed) e che io ho sintetizzato denota il minimo comune denominatore ad ogni possibile versione concreta del linguaggio di programmazione Java.
Il fatto che i componenti dell'array nel tuo esempio riflettano un mutamento non significa che tu possa far affidamento sul fatto che tale mutamento si verifichi per ogni versione della piattaforma Java. Tutto ciò di cui puoi e devi tener conto è quanto detto nelle specifiche.
Nel caso che proponi le specifiche dicono che (la prima azione di un Thread è sincronizzata con l'ultima azione del Thread che lo avvia) per i due Thread diversi dal main "array" è correttamente inizializzato, cioè sia il main che i due Thread da questo avviati giocano con puntatori allo stesso array.
Rinvio al capitolo delle citate specifiche in modo tale da consentire una verifica su quanto ho detto (ovviamente correggetemi se sbaglio).
http://java.sun.com/docs/books/jls/third_edition/html/memory.html
nuovoUtente86
18-10-2007, 19:00
Ora vado a leggere le specifiche.Ma molto rudemente per ottenere in modo certo il risultato voluto nel codice in questione avrei dovuto usare la parola chiava volatile?
Saresti cosi gentile da postare un esempio codificato in cui non si ha modifica globale ma solo privata al thread.Grazie.
P.s L' argometo è molto interessante,purtroppo sia nell' approccio didattico che su libri come il Core Java veniene taciuto...Si parla di thread...di sincronizzazione,atomicità ecc..ma di tale aspetto no.
nuovoUtente86
19-10-2007, 12:31
.
Nel caso che proponi le specifiche dicono che (la prima azione di un Thread è sincronizzata con l'ultima azione del Thread che lo avvia) per i due Thread diversi dal main "array" è correttamente inizializzato, cioè sia il main che i due Thread da questo avviati giocano con puntatori allo stesso array.
nn ho ben capito cosa vuoi dire.
volatile è uno dei meccanismi di sincronizzazione. Due o più flussi di controllo concorrenti (cioè due liste parzialmente ordinate di istruzioni in esecuzione) si dicono sincronizzati quando la loro esecuzione corrisponde a quella di un unico ipotetico flusso di controllo.
Lo dico perchè "sincronizzazione" ha un po' quell'aura di mistero, come termine. Invece è una banalità.
Ciò premesso, ecco un caso in cui occorre sincronizzare.
public class Bingo {
private int value = 10;
public int getValue() { return value; }
}
Il Thread A crea un Bingo.
Bingo b = new Bingo();
In seguito, il Thread B invoca:
b.getValue();
Quale valore è garantito che il Thread B ottenga invocando b.getValue()? Secondo le specifiche, il risultato dell'invocazione di b.getValue() è zero.
Può essere 10 ma non è certo nel senso che una versione di Java potrebbe restiture 10 e una versione diversa potrebbe restituire zero ed entrambe sarebbero conformi alle specifiche.
La ragione per cui il Thread B può leggere zero sta in ciò che il modello di memoria del linguaggio di programmazione Java garantisce che la lettura del valore di un campo avvenga dopo l'inizializzazione di quel campo al valore di default (zero, null, false).
Ora, se io volessi far leggere al Thread B il valore 10, cosa potrei fare? Ci sono diverse soluzioni. Una è l'uso della parola chiave volatile.
public class Bingo {
private volatile int value = 10;
public int getValue() { return value; }
}
Bingo b = new Bingo(); //Thread A
b.getValue(); //Thread B: vede 10.
Qui volatile funziona perchè, in chiaro, forza il Thread A a scaricare nell'heap il risultato della scrittura value = 10. Deve farlo perchè, un po' meno chiaramente, le specifiche impongono una sincronizzazione tra la scrittura di una variabile condivisa marcata volatile ed una successiva lettura di quella stessa variabile da parte di un Thread diverso da quello abbia scritto. LE specifiche dicono cioè che il caso:
flusso di controllo 1: value = 10
flusso di controllo 2: b.getValue()
equivale all'unico flusso:
value = 10;
getValue();
ogni volta che value = 10 (cioè Bingo b = new Bingo()) sia eseguito prima di b.getValue().
Occorre tenere a mente questo "sia eseguito prima". L'analogo non sincronizzato:
flusso di controllo 1: value = 10
flusso di controllo 2: b.getValue()
può equivalere al flusso:
value = 10;
getValue();
o, indifferentemente, al flusso:
getValue();
value = 10;
a prescindere dal fatto che value = 10 sia eseguito prima di getValue(). Questo fatto, che può apparire quantomeno bizzarro, è esattamente il frutto del possesso di una copia locale (al Thread) di ogni variabile condivisa (cioè di ogni campo).
E questo è l'inizio e la fine della concorrenza in Java: è tutto qui. In più ci sono solo le diverse regole che ci dicono se è possibile trovare un flusso sequenziale corrispondente a più flussi paralleli e come è fatto quel flusso. Ad esempio nel nostro Bingo potremmo usare "final" al posto di "volatile" ed ottenere lo stesso effetto. Ma potremmo anche ottenere un'analoga sincronizzazione sfruttando la regola secondo cui l'avvio di un Thread è sincronizzato con la prima operazione di quel Thread, cioè:
public static void main(String[] args) {
//Thread main
final Bingo bingo = new Bingo();
//Thread diverso dal main
new Thread() { public void run() { b.getValue(); }}.start();
}
Qui, sempre secondo le specifiche, l'avvio del Thread, cioè l'invocazione del suo metodo start(), precede la lettura del valore di "b" (lettura necessaria per l'invocazione del metodo.
Considerando che:
le azioni inter-thread (cioè le azioni che sono eseguite da un solo Thread) devono rispettare l'effetto che avrebbero se fossero eseguite nello stesso ordine in cui appaiono nel codice - cioè b = new Bingo è eseguito prima di new Thread() che è eseguito prima di start()
la relazione d'ordine tra azioni concorrenti è transitiva (se a capita prima di b e b capita prima di c allora a capita prima di c)
risulta che:
l'assegnamento value = 10 operato dal Thread Main è necessariamente visibile al secondo Thread quando quest'ultimo esegue getValue().
Comunque il punto di riferimento più affidabile in materia resta il capitolo 17 delle specifiche nella loro terza edizione (che a sua volta chiarisce quanto più discorsivamente e, forse, chiaramente, esposto nella seconda edizione).
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.