|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Member
Iscritto dal: Sep 2008
Città: Padova
Messaggi: 172
|
[Java] Gestione Threads
Salve ragazzi, devo realizzare un programma in Java che mediante la gestione dei threads incrementa e decrementa una variabile.
Mi son costruito 4 classi.. sono alle prime armi e nonostante il problema sia semplice per me non è immediatissimo. Il main: Codice:
public class main {
public static void main(String[] args) {
Thread thread = new Thread();
Incrementa incrementa = new Incrementa(thread);
Decrementa decrementa = new Decrementa(thread);
Thread aggiungo = new Thread(incrementa);
Thread sottraggo = new Thread(decrementa);
aggiungo.start();
sottraggo.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
}
}
Codice:
public class Thread {
int numeri = 0;
public void plus() {
numeri++;
}
public void minus() {
numeri--;
}
}
Codice:
public class Incrementa implements Runnable {
Thread th;
public Incrementa(Thread th) {
this.th = th;
}
public void run() {
th.plus();
}
}
Codice:
public class Decrementa implements Runnable {
Thread th;
public Decrementa(Thread th) {
this.th = th;
}
public void run() {
th.minus();
}
}
__________________
PACKARD BELL Easy Note TJ75@CPU INTEL Core i5 430M //GPU ATI RADEON HD 5470//RAM CORSAIR 4GB DDR3// HD WESTERN DIGITAL 640GB SATAII 3.0 GB/s |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Cambia il nome della tua classe Thread in Contatore.
Aggiungi il modificatore volatile al campo numeri di Contatore: private volatile int numeri = 0; Dopodichè il programma aumenta e diminuisce di uno il valore di contatore e termina. Codice:
public class Main {
public static void main(String[] args) {
Contatore contatore = new Contatore();
Incrementa incrementa = new Incrementa(contatore);
Decrementa decrementa = new Decrementa(contatore);
Thread aggiungo = new Thread(incrementa);
Thread sottraggo = new Thread(decrementa);
aggiungo.start();
sottraggo.start();
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
ex.printStackTrace();
return;
}
System.out.println("Contatore vale: " + contatore.numeri);
}
}
class Contatore {
volatile int numeri = 0;
public void plus() {
numeri++;
}
public void minus() {
numeri--;
}
}
class Incrementa implements Runnable {
Contatore th;
Incrementa(Contatore th) {
this.th = th;
}
public void run() {
th.plus();
}
}
class Decrementa implements Runnable {
Contatore th;
Decrementa(Contatore th) {
this.th = th;
}
public void run() {
th.minus();
}
}
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#3 |
|
Member
Iscritto dal: Sep 2008
Città: Padova
Messaggi: 172
|
Cosa sta a significare "Volatile" non l'ho mai sentita come istruzione.. Grazie della precisa risposta
La cosa strana che rimane come valore sempre 0.. perchè? non incrementa la variabile?
__________________
PACKARD BELL Easy Note TJ75@CPU INTEL Core i5 430M //GPU ATI RADEON HD 5470//RAM CORSAIR 4GB DDR3// HD WESTERN DIGITAL 640GB SATAII 3.0 GB/s Ultima modifica di CertainDeath : 12-10-2009 alle 19:12. |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
volatile serve per obbligare i thread a leggere il valore di un campo dalla memoria condivisa e per obbligare gli stessi thread ad aggiornare quella memoria quando cambiano il valore di quel campo.
Senza volatile, e non usando altri meccanismi di sincronizzazione, quando un thread "legge" un campo è libero di non tener conto del valore che quel campo ha e, quando "scrive su" un campo, è libero di non aggiornare la regione di memoria condivisa corrispondente a quel campo.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#5 | |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Quote:
Nota che i due Thread non eseguono dei cicli di incremento o decremento: uno fa un +1 e schiatta subito dopo, l'altro fa un -1 e si defila altrettando rapidamente. E' come se ci fosse scritto: Codice:
int valore = 0; valore++ valore--; System.out.println(valore);
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
|
#6 |
|
Member
Iscritto dal: Sep 2008
Città: Padova
Messaggi: 172
|
Per fare dei cicli di incremento e decremento posso usare Synchronized? oppure devo usare i cicli if.. ecc?
__________________
PACKARD BELL Easy Note TJ75@CPU INTEL Core i5 430M //GPU ATI RADEON HD 5470//RAM CORSAIR 4GB DDR3// HD WESTERN DIGITAL 640GB SATAII 3.0 GB/s |
|
|
|
|
|
#7 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
I cicli li fai coi cicli
synchronized, volatile, pipicchio e pipacchio ti interessano solo nel momento in cui riscontri che il tuo Thread sta cercando di accedere ad un valore che non è stato lui a creare. Tutto il resto è immutato, i for sono for, gli if sono if, gli interi sono interi eccetera.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Mar 2007
Messaggi: 7863
|
Secondo le specifiche però, in questo caso, volatile non dovrebbe garantire l' atomicità in quanto il valore della variabile dipende dal precedente (trattandosi di un contatore).
|
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
L'unica atomicità garantita da volatile è sulle letture o scritture dei valori a 64bit. Per il resto tutte le letture e scritture sono atomiche di loro.
Che quello sia un contatore è tutto da verificare: ho messo il nome che m'è venuto ma in origine era un Thread.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Mar 2007
Messaggi: 7863
|
Ho ripreso il termine contatore giusto perchè lo avevi già utilizzato: potrebbe rappresentare un' istantanea degli utenti su una coda o qualsiasi altra cosa ma non è questa la cosa importante.
Sicuramente read e write (tranne che per long e double)sono garantiti dalla jvm, ma numeri++(numeri=numeri+1) e numeri-- non sono eseguite in maniera indivisibile (come invece garantisce qualche compilatore c++). In questo caso il risultato risulta corretto forse più per la sincronizzazione sull' ultima istruzione del thread che lancia (ma non ne sono affatto sicuro) che per l' utilizzo di volatile. Se mettissimo i cicli e lanciassimo più di 2 thread il risultato non credo sarebbe coerente. Ultima modifica di nuovoUtente86 : 13-10-2009 alle 01:49. |
|
|
|
|
|
#11 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Come dicono i francesi, calma e gesso
A me volatile interessa perchè senza quel "System.out.println(contatore.numeri)" può non riflettere le mutazioni che i due altri thread producono su "numeri". Dopodichè, assumendo che i due thread aggiungo e sottraggo terminino la loro esecuzione in quel secondo di attesa del main, stamperà o zero o uno o meno uno.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Mar 2007
Messaggi: 7863
|
Su questo non ci piove, ma presumo che intrinsecamento lo scopo dell' esercizio fosse anche attuare una politica di sincronizzazione, che molto grezzamente (forzando alla fine la sequenzialità) poteva essere garantita imponendo una join(), ma meglio con l' utilizzo di AtomicInteger.
A proposito del package Atomic volevo chiederti se lo stesso garantisce l' atomicità anche su architetture multicore ovvero se un eventuale thread che esegue su un altro core venga sospeso se tenta l' accesso ad una variabile impegnata in una operazione atomica non ancora conclusa? |
|
|
|
|
|
#13 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Sì, gli AtomicXYZ definiscono anche operazioni atomiche nel senso che i vari getAndSet, setAndGet, addAndSet eccetera sono unitarie.
A meno di bug - e nella bugparade qualcuno ce n'era - l'architettura è sempre irrilevante. Se noti tutto ciò che si trova nel package concurrent spiega i propri effetti in relazione alle norme del java memory model e il JMM dice che se una cosa si chiama "Java" allora deve fare una certa cosa, a prescindere dal sistema ospite. Come si faccia concretamente è esemplificato in http://gee.cs.oswego.edu/dl/jmm/cookbook.html
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#14 |
|
Senior Member
Iscritto dal: Mar 2007
Messaggi: 7863
|
si effettivamente far dipendere il comportamento dalla piattaforma avrebbe negato l' essenza stessa di JAva.
Ragionando un po sui thread, mi è sorto un dubbio ovvero se nella work area privata di ogni thread, in caso di oggetti che ne puntano altri, questi vengano mantenuti i riferimenti (quindi mantenuti in shared) oppure si clonano effettivamente nella zona privata tutti gli oggetti presenti sulla catena di reference |
|
|
|
|
|
#15 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
La metafora della copia vale solo per il valore delle variabili condivise che, per un reference, sarebbe il valore del puntatore. Eventuali riferimenti indiretti collegati a quel puntatore sono a loro volta soggetti alle norme del modello di memoria. E' il tipico caso della variabile locale. Se ho un thread che dichiara e inizializza localmente un punto:
Codice:
new Thread() {
public void run() {
Point p = new Point(10, 20);
altroThread.valuta(p);
}
}.start();
Il problema è che se p è locale NON sono locali i campi x e y accessibili tramite p e dunque il loro valore sarà o non sarà 10 e 20 a seconda che per x e y si possano dire rispettate le norme sulla sincronizzazione delle letture con le scritture - nel caso specifico di Point non lo sono e l'unica garanzia che si ha è che p sia "properly constructed", cioè x e y varranno certamente almeno zero. E' lì che entra in gioco la famosa relazione "happens-before" che va sempre valutata sia nel rapporto intre-thread che nel rapporto intra-thread. Ad esempio se in un Thread A avessimo: Codice:
Point p = new Point();
p.y = 300;
synchronized(p) {
p.x = 10;
}
threadB.valuta(p);
...ThreadB.valuta
synchronized(p) {
int x = p.x;
int y = p.y
}
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#16 |
|
Senior Member
Iscritto dal: Mar 2007
Messaggi: 7863
|
Quindi se abbiamo un array di int su cui operano diversi thread, la "copia" locale ad ogni thread riguarda il reference all' oggetto piuttosto che il vettore stesso.
L' esempio non è casuale, perchè ricordo una vecchia discussione, su un altro forum, in cui si parlava proprio di assegnamenti del tipo v[posizione]=10; non scaricati sulla memoria dell' applicazione, eppure mantenendo copia solo del reference non dovrebbe sussistere il problema. |
|
|
|
|
|
#17 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
E' tutto locale, nel senso che se ho un riferimento ad un array
x senza sincronismi ogni thread ha il suo riferimento x. Quando il thread agisce sul componente [0] agisce su una copia locale del componente di un array locale. Se garantiamo la condivisione del riferimento all'array x allora ogni volta che il thread va a leggere x legge l'ultimo valore che questo x ha (cioè se qualcuno redirige il riferimento verso un altro array allora il thread agirà su quell'array). Questo però non si estende ai componenti di quell'array: x[1] sarà un valore locale che il thread non è tenuto ad inizializzare con lo stesso valore che ha l'array a cui x punta. Esempio. final int[] array = new int[20]; final ci dice che chiunque legga array lo vedrà come array di 20 componenti ognuno dei quali vale zero. 1) thread 1: array[0] = 20; 2) thread 2: System.out.println(array[0]); thread 2 può stampare zero perchè quando thread 1 scrive sul componente 0 di array non è tenuto a far sì che tale scrittura sia visibile ai thread che leggerano quel valore. E questo nonostante sia thread 1 che thread 2 vedano "array" puntare alla stessa regione di memoria.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#18 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Qui c'è una serie molto carina di Sun sulla programmazione parallela
http://channelsun.sun.com/video/an+i...g+/25947206001 la segnalo perchè c'è un punto in cui si "vede" questa faccenda della località dei riferimenti detenuti dai thread nelle architetture multicore dove lungo la strada che dal programma nella memoria principale arriva al singolo core a un certo punto la memoria cessa di essere condivisa e "ognuno fa da sè".
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#19 |
|
Senior Member
Iscritto dal: Mar 2007
Messaggi: 7863
|
E se usassimo un' ArrayList di Integer in luogo dell' array avremmo lo stesso comportamento?
|
|
|
|
|
|
#20 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Sì, vale in generale per ogni cosa che si trovi nella memoria condivisa.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 23:37.




















