Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Mova Z70 Ultra Roller Complete: motore potente, rullo di lavaggio e l'IA a guidare
Mova Z70 Ultra Roller Complete: motore potente, rullo di lavaggio e l'IA a guidare
Mova Z70 Ultra Complete è un robot aspirapolvere che coniuga un'aspirazione potente e un lavaggio con rullo a logica di intelligenza artificiale che guida al meglio nella pulizia di casa: rulli e spazzole estensibili a pulire gli angoli e una base di ricarica che lava e ripristina il robot al emglio delle sue funzionalità dopo ogni azione di pulizia
Recensione Google Pixel 11: non ha l'HiLight dei Pro, ma è il Pixel più equilibrato di sempre
Recensione Google Pixel 11: non ha l'HiLight dei Pro, ma è il Pixel più equilibrato di sempre
Abbiamo provato Google Pixel 11, il più accessibile della nuova gamma: chip Tensor G6 condiviso con i modelli Pro, fotocamera 48 MP con Magic Capture e Stili Fotografici, display Actua da 3000 nit e batteria da 4985 mAh. Ecco come si comporta nell'uso quotidiano, e cosa cambia davvero rispetto a Pixel 11 Pro e Pro XL
Google Pixel 11 Pro XL: fotocamera al top, batteria indietro. Luci e ombre del nuovo flagship
Google Pixel 11 Pro XL: fotocamera al top, batteria indietro. Luci e ombre del nuovo flagship
Google Pixel 11 Pro XL debutta in Italia con il nuovo Tensor G6, lo Zoom Pro fino a 120x, il display Super Actua da 3600 nit e la new entry HiLight riservata ai modelli Pro: lo abbiamo provato in anteprima per diversi giorni prima del lancio commerciale, tra fotocamera generativa, ricarica ancora indietro rispetto ai rivali e un prezzo che parte da 1399 euro
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 13-10-2009, 21:12   #21
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Ora la situazione si sta schiarendo ma resta ancora qualche dubbio e riprendo il tuo esempio di prima:
se abbiamo
int[] array = new int[20];

la variabile array conterrà l' indirizzo, sull' heap del programma, in cui il vettore è ospitato.
I thread che opera sull' array avranno una variabile di tipo reference array_privato che se ho ben capito(ed è proprio questo il dubbio), punterà (almeno inizialmente) proprio all' indirizzo sull' heap globale. Però a questo punto non capisco dove vengono fatte le scritture del tipo array[0]=1; che hanno scope locale ai thread.
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 13-10-2009, 22:08   #22
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Su variabili locali al thread, di tipo uguale a quello del componente dell'array, il cui valore iniziale è il valore predefinito dei campi non inizializzati.

Occhio che senza alcun intervento esterno quando l'array deve operare su "array" non è tenuto ad andare a vedere cosa c'è nella regione di memoria a cui punta la variabile array - cioè il valore della variabile.

E' solo obbligato ad operare su una variabile del tipo di array. Cioè:

int[] array = new int[20];

...
Thread 1: array[0] = 20; //può benissimo rilasciare una NullPointerException

Mentre:

final int[] array = new int[20];
...
Thread 1: array[0] = 20; //non rilascerà mai una NullPointerException

Il final obbliga Thread 1: ad andare a guardare dove punta array, e non punta a null. Vedrà che ha dei campi. Non è obbligato ad andare a vedere il valore di quei campi. Quindi se io dicessi:

final int[] array = new int[20];
array[0] = 300;
...
Thread 1: print(array[0]); //Mai NullPointer ma può stampare zero anzichè 300

Vale anche per volatile, sebbene questo sia più "forte" di final:

volatile int[] array = new int[20]
array[0] = 300;

Le specifiche dicono che la scrittura di un valore volatile happens-before la lettura di quel valore.

"A write to a volatile variable v synchronizes-with all subsequent reads of v by any thread"

Qui "v" è "array".

Per la transitività, ciò che in uno stesso thread capita prima della scrittura di un valore volatile capita anche prima della lettura di quel valore. Ma è sempre lettura o scrittura del valore della variabile volatile. E se andiamo a guardare l'operazione di assegnamento ad un componente di un array vediamo che:

1. legge il valore "array"
2. non scrive sul valore "array" ma attraverso il valore array sul campo [0]

C'è una lettura di "array" (il valore volatile) che precede una scrittura sul campo [0]. Quando, più tardi, accade:

Thread 1: int y = array[0];

Essendo array volatile Thread 1 è obbligato a vedere il valore che array ha in forza dell'ultima scrittura. E l'ultima scrittura sulla variabile "array" è "new int[20]": la scrittura su array - e non tramite array - è il punto che, per una variabile volatile - definisce l'ordine di sincronizzazione.

Paradossalmente, per via della semantica di volatile e della transitività, obbligheremmo Thread 1 a vedere 300 se dicessimo:

volatile int[] array = new int[20];
array[0] = 300;
array = array;//scrittura su array
...
Thread 1: println(array[0]); //deve stampare 300

Deve stampare 300 perchè:

1. le azioni intra-thread capitano nell'ordine in cui appaiono nel codice.

Quindi array[0] = 300; happens-before array = array.

2. la scrittura di un valore volatile da parte di un thread A happens-before la lettura su quel valore da parte di un thread B:

quindi in Thread 1 "array" (la lettura) capita dopo array = array che capita dopo array[0].

in Thread 1, array[0] capita dopo la lettura di array. Assumendo che Thread 1 esegua le sue istruzioni dopo il thread che dichiara e e cambia il valore di array, il risultato che vedremo dovrà essere quello che avremmo se eseguissimo questo unico flusso:

volatile int[] array = new int[20];
array[0] = 300;
array = array;//scrittura su array
println(array[0]);


Effetto "accademico" di volatile perchè, come per l'incremento ++ o il decremento --, in un contesto più realistico non è possibile assumere che un Thread interverrà su array solo dopo "array = array" ma si introdurranno degli altri meccanismi di sincronizzazione che, in genere, garantiscono la visibilità e qualcos'altro.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 13-10-2009, 22:46   #23
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Quote:
Originariamente inviato da nuovoUtente86 Guarda i messaggi
la variabile array conterrà l' indirizzo, sull' heap del programma, in cui il vettore è ospitato.
I thread che opera sull' array avranno una variabile di tipo reference array_privato che se ho ben capito(ed è proprio questo il dubbio), punterà (almeno inizialmente) proprio all' indirizzo sull' heap globale.
Quindi questo ragionamento non è del tutto corretto? O meglio lo è ma come dicevi, se ho ben capito, i thread non hanno l' obbligo di sincronizzarsi sul valore globale ma sono tenuti solo al corretto utilizzo dei tipi.
Quindi se io avessi
ArrayList ar =new ArrayList(10);
for(......)
ar.add(...);

e un thread deve operare un' aggiunta
ar.add(new Integer(10));
potrebbe benissimo farla su una ArrayList vuota.

Ultima modifica di nuovoUtente86 : 13-10-2009 alle 22:52.
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 13-10-2009, 22:57   #24
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Non è corretto. La regola è che un qualsiasi Thread è tenuto a vedere un qualsiasi campo inizializzato al suo valore di default (0, null, false).

Poi si va a vedere se sussistono delle condizioni particolari che obbligano il Thread a leggere il valore effettivo di quel campo.

Vale per i campi singolarmente considerati. Se A è un campo attraverso cui è possibile accedere ad altri campi, un eventuale obbligo per un thread di vedere il valore attuale di A non comporta necessariamente l'obbligo di vedere i valori attuali dei campi accessibili tramite A.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 13-10-2009, 22:59   #25
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Quote:
Originariamente inviato da nuovoUtente86 Guarda i messaggi
ArrayList ar =new ArrayList(10);
for(......)
ar.add(...);

e un thread deve operare un' aggiunta
ar.add(new Integer(10));
potrebbe benissimo farla su una ArrayList vuota.
Può cercare di farlo su un "ar" che vale null.

Se per una qualche ragione (final, volatile o altro) è tenuto a vedere il valore di "ar" ciò non significa che sia tenuto a rispettare il valore dei campi accessibili tramite "ar".
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 00:27   #26
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Quindi in realtà dichiarare una variabile reference volatile o sincronizzarla con qualche altro strumento del linguaggio garantisce che, un eventuale riassegnamento (ar=new ArrayList), sia visto da tutti i thread concorrenti ma non assicura nulla circa operazioni come add e remove ad esempio.
Mi viene ora in mente che le comuni implementazioni del produttore/consumatore(classi esempio di multithreading) potrebbero (in realtà quasi tutte le implementazioni poi funzionano) non funzionare per via dell' utilizzo del buffer private.
Ma a questo punto come garantire che operazioni come array[0]=1; o arrayl.add(qualcheOggetto); abbiano effettivamente visibilità sull' heap globale?
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 01:10   #27
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
E' corretto dire che volatile non garantisce in assoluto la visibilità delle mutazioni che coinvolgono riferimenti indirettamente accessibili attraverso la variabile volatile.

Tuttavia rendere quelle mutazioni visibili è piuttosto facile.

Considera ad esempio synchronized. Il JMM dice che l'uscita da un blocco sincronizzato su un monitor M happens-before l'entrata in un blocco sincronizzato sullo stesso monitor M - da parte di un altro Thread.

Supponiamo che due Thread condividano lo stesso monitor M, sia esso un riferimento ad hoc

final Object M = new Object(); //sempre final

o un Lock:

final Lock M = new ReentrantLock();

o lo stesso reference che un Thread vuole passare ad un altro, a patto che ne sia un riferimento locale. Per via della transitività della relazione HB questo:

final Object M = new Object();

1.) Thread 1: Point p = new Point(10, 20);
2.) Thread 1: ArrayList<Point> list = new ArrayList<Point>();
3.) Thread 1: list.add(p);
4.) Thread 1: synchronized(M) {}

seguito da:

5.) Thread 2: synchronized(M) {}
6.) Thread 2: Point x = list.get(0);
7.) Thread 2: System.out.println("(" + p.x +"," + p.y + ")");

stampa sempre

(10, 20)

Proprio per via della transitivià. L'entrata di Thread 2 nel blocco sincronizzato è successiva, quanto alla visibilità delle letture e delle scritture, all'uscita da quel blocco da parte di Thread 1. Tutte le letture e scritture che Thread 1 esegue prima di uscire dal blocco sincronizzato avvengono in uno stesso thread quindi accadono nell'ordine in cui si vedono nel codice. Tutte le scritture e letture che Thread 2 esegue dopo l'entrata nel blocco sincronizzato sono parimenti operazioni eseguite in un solo thread quindi capitano nell'ordine del codice.

E' la transitività della relazione happens-before a rendere le cose facili.

Il problema è che la sincronizzazione richiede operazioni molto costose lato CPU - si parla delle più onerose in assoluto.

Non è sempre necessaria. Ad esempio sappiamo dal JMM che l'azione che avvia un thread happens-before la prima azione (cioè la prima lettura o scrittura) eseguita dal nuovo Thread.

Ciò significa che un caso come questo:

Codice:
List<Point> list = new ArrayList<Point>(); //ipotetico campo
...crea un marea di punti e infilali in list

new Thread() {
    public void run() {
        for(Point p : list) {
            fai qualcosa con p
        }
    }
}.start();
Non richiede alcun synchronized, final o volatile per funzionare, perchè "start" capita prima della prima istruzione nel nuovo Thread e tutte le operazioni sulla lista capitano prima di quello start - perchè sono azioni intra-thread.

E ci sono poi le utilità del package collection. Una delle garanzie di ConcurrentLinkedQueue o di CopyOnWriteArrayList eccetera è che i "put" e gli "add" capitano prima dei "get". Quindi data un'ipotetico campo-coda:

final ConcurrentLinkedQueue<Point> coda = new ConcurrentLinkedQueue<Point>();

Thread 1: Point p = new Point();
Thread 1: p.x = una tragedia di conti che coinvolge milioni di altri campi
Thread 1: coda.offer(p);

e letto in seguito da un altro

Thread 2: Point p = coda.take();

è "correttamente sincronizzato", nel senso che i valori x e y, indirettamente accessibili tramite p, saranno letti per il valore che effettivamente hanno nell'heap.

perchè il campo coda deve essere final? Perchè ConcurrentLinkedQueue dice che "take" capita dopo "offer" ma prima di "take" Thread 2 compie un'azione, la lettura del campo "coda", e questa non è successiva al take.

A conti fatti bisogna semplicemente prestare attenzione a qualche piccolo dettaglio ma il grosso della creazione di programmi multithread Java correttamente sincronizzati è relativamente gestibile.

E non c'è il problema di andare a guardare come funzioni questo o quel compilatore, questa o quella architettura.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 01:41   #28
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Quote:
Proprio per via della transitivià. L'entrata di Thread 2 nel blocco sincronizzato è successiva, quanto alla visibilità delle letture e delle scritture, all'uscita da quel blocco da parte di Thread 1. Tutte le letture e scritture che Thread 1 esegue prima di uscire dal blocco sincronizzato avvengono in uno stesso thread quindi accadono nell'ordine in cui si vedono nel codice. Tutte le scritture e letture che Thread 2 esegue dopo l'entrata nel blocco sincronizzato sono parimenti operazioni eseguite in un solo thread quindi capitano nell'ordine del codice.
Parto dalle premessa che abbiamo fatto qualche post fa ovvero che un thread garantisce solo di accedere alle "copie" private inizializzate ai valori di defautl. Quindi presumo o meglio deduco dal tuo ultimo post che siano gli strumenti di sincronizzazione a garantire che il thread2 stampi le coordinate del punto (contenute sul segmento globale)e non faccia una system.out su un punto che punta a NULL.

Quote:
final ConcurrentLinkedQueue<Point> coda = new ConcurrentLinkedQueue<Point>();

Thread 1: Point p = new Point();
Thread 1: p.x = una tragedia di conti che coinvolge milioni di altri campi
Thread 1: coda.offer(p);

e letto in seguito da un altro

Thread 2: Point p = coda.take();

è "correttamente sincronizzato", nel senso che i valori x e y, indirettamente accessibili tramite p, saranno letti per il valore che effettivamente hanno nell'heap.
anche in questo caso credo sia il locking interno a forzare il flushing dei dati sull' heap?!
Perchè l' happens before ci assicura l' ordine delle azione, ma non credo che indichi nulla circa la località o globalità dei dati.

Ultima modifica di nuovoUtente86 : 14-10-2009 alle 01:45.
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 13:18   #29
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Mhh... non ho capito la domanda

Dal punto di vista del linguaggio la ragione per cui quel codice, nel presupposto che Thread 2 esegua quelle istruzioni dopo Thread 1, è correttamente sincronizzato, deriva dal fatto che sussitono le relazioni happens-before citate.

Spetta poi alla macchina virtuale fare in modo che quelle regole del linguaggio di programmazione Java siano "fisicamente" rispettate.

La località è metaforica. Una JVM che semplicemente dica ai Thread di andare sempre e comunque ad aggiornare la memoria condivisa rispetta il modello di memoria. E non ci sarebbe alcuna località. Ma anche una JVM che solo sull'offer e sul take emettesse un'istruzione (mfence o mf o mb o msync) rispetterebbe l'happens-before mentre le istruzioni precedenti e successive sarebbero libere di agire sui valori eventualmente contenuti nella cache del processore.

Non so però se questo fosse il dubbio.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 15:27   #30
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Quote:
Non richiede alcun synchronized, final o volatile per funzionare, perchè "start" capita prima della prima istruzione nel nuovo Thread e tutte le operazioni sulla lista capitano prima di quello start - perchè sono azioni intra-thread.
provo a dare un' interpretazione grossolana: all' atto dello start il thread si sincronizza(anche sui valori referenziati indirettamente) con la situazione heap creata dalle operazioni del thread che lo lancia che più formalmente dovrebbe essere questa parte di specifica

Codice:
An action that starts a thread synchronizes-with the first action in the thread it starts. 
The write of the default value (zero, false or null) to each variable synchronizes-with the first action in every thread. Although it may seem a little strange to write a default value to a variable before the object containing the variable is allocated, conceptually every object is created at the start of the program with its default initialized values.
Che poi la seconda parte dovrebbe essere quella relativa ai valori di default già anticipata qualche post fa.
spero di non aver detto schiocchezze.

Quote:
è "correttamente sincronizzato", nel senso che i valori x e y, indirettamente accessibili tramite p, saranno letti per il valore che effettivamente hanno nell'heap.

perchè il campo coda deve essere final? Perchè ConcurrentLinkedQueue dice che "take" capita dopo "offer" ma prima di "take" Thread 2 compie un'azione, la lettura del campo "coda", e questa non è successiva al take.
ho compreso il discorso sull' happens before, ma il dubbio è perchè l' istruzione coda.offer(p), che deve avvenire prima di take() (e fin qui ci siamo), scarica i valori correnti sull' heap e non su una sua variabile privata, come invece abbiamo assunto per il v[1]=10; sul vettore dei precedenti post.
Quello che mi viene da pensare è che le regole di happens-before costituiscono punto di sincronizzazione.
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 15:48   #31
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Il perchè è, banalmente, perchè lo dice la javadoc, cioè fa parte del contratto delle collezioni concurrent. Poi se andiamo a guardare il codice, ad esempio di CopyOnWriteArrayList, vediamo che per certe operazioni usa un Lock, il cui contratto dichiara un'equivalenza semantica con synchronized(X), per altre si avvale di condizioni happens-before stabilite nel JMM che non richiedono l'uso di modificatori o primitive di sincronizzazione (un po' come thread.start() è sincronizzato con la prima lettura o scrittura eseguita dal thread senza il bisogno di ulteriori aggiunte).

Lo stesso fa coda.offer. Codice alla mano, usa AtomicReferenceUpdater che è una "capsula" in grado di garantire la visibilità inter thread delle mutazioni occorse ai campi di un dato riferimento.

Ma sono tutti dettagli di realizzazione, quello che interessa a noi "utenti", del linguaggio o delle sue librerie, è che talune operazioni sono, in senso lato, "thread-safe".

Quote:
Quello che mi viene da pensare è che le regole di happens-before costituiscono punto di sincronizzazione.
E' il contrario ma siamo lì. il JMM parte dicendo che si sono alcune operazioni definite come actions - principalmente letture e scritture.

Di queste talune sono intra-thread, talaltre inter-thread.

A certe condizioni (lo start, il final, il volatile, il synchronized, lo yield, il wait-notify eccetera) le azioni inter-thread sono da considerarsi sincronizzate.

Da ultimo, se un'azione A è sincronizzata con un'azione B allora l'implementazione deve fare in modo che l'esecuzione del programma manifesti l'accadimento di A prima di B.

Come è irrilevante: se un thead dice che x vale uno e un thread dice che x vale x + 1 e dal codice sorgente risulta che queste due espressioni accadono una prima dell'altra - perchè comportano azioni, quelle azioni sono sincronizzate, e quindi A happens-before B - allora in un qualsiasi modo alla fine x dovrà valere 2.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 16:25   #32
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Codice:
public class Test{
public static void main(String[]args){
int[] array={10,10,10,10,10};
new ProvaThread(array,1).start();
new ProvaThread(array,2).start();
}}
in questo caso dovrei aver la garanzia che sia thread1 che che thread2 si soncronizzino con il main e quindi vedano l' array inizializzato correttamente e se nel run di ognuno facessi una stampa di uno degli elementi otterrei 10. E' corretto?
Se però invece di una stampa facessi un' assegnamento array[1]=7; che comportamento si avrebbe?

e ancora se modificassi il codice con qualcosa del genere

Codice:
public class Test{
public static void main(String[]args){
int[] array=new int[10];
Thread t1=new ProvaThread(1).start();
Thread t2=new ProvaThread(2).start();//codice non corretto ma rende l' idea dello start
array[x]=10;
.
.
.riempimento dell' array
t1.setArray(array);
t2.setArray(array);
}}
In questo caso una stampa, da specifiche, garantisce uno 0, giusto?
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 16:38   #33
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Direi che è corretto, anche se il secondo caso potrei non averlo capito tanto bene.

Il primo è correttamente sincronizzato per via dello "start": ciò che è stato fatto prima dello start è visibile nel run del thread.

Se intendo correttamente il secondo caso, anche qui la tua deduzione è corretta. Il main cambia un valore dell'array dopo lo start dei thread. C'è ancora il punto di sincronizzazione nello start solo che stavolta c'è anche una scrittura successiva a quel punto a fronte della quale lo "start" non è più rilevante.

Al che sorge l'interrogatorio: se volessimo obbligare i due thread a vedere quel valore 10 per il componente x di array, come potremmo fare?
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 16:48   #34
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Una soluzione l' avevi proposta qualche post fa: rendere volatile array e fare un assegnamento array=array. Oppure usare qualche meccanismo esplicito di locking.

Nel primo blocco di codice postato,non mi è chiarissimo cosa avviene se i thread invece di una stampa cambiano qualche cella dell' array.
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 17:17   #35
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Quote:
Originariamente inviato da nuovoUtente86 Guarda i messaggi
Una soluzione l' avevi proposta qualche post fa: rendere volatile array e fare un assegnamento array=array. Oppure usare qualche meccanismo esplicito di locking.
Ancora più banale: spostare quella istruzione prima della creazione (e successivo start) dei due thread
(Scherzo, sto seguendo con molto interesse il thread )
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 19:21   #36
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
Quote:
Originariamente inviato da nuovoUtente86 Guarda i messaggi
Nel primo blocco di codice postato,non mi è chiarissimo cosa avviene se i thread invece di una stampa cambiano qualche cella dell' array.
Be', vediamo.

Main-Thread: crea l'array e lo inizializza con dei valori. Questa inizializzazione comporta la scrittura di valori sull'heap
Main-Thread: avvia Thread 1. L'avvio è sincronizzato con le azioni precedenti che, quindi, happen-before la prima azione di Thread 1
Main-Thread: avvia Thread 2. Come sopra.

Thread 1: legge un campo - un componente dell'array. E' un'azione inter-thread. Abbiamo visto che è sincronizzata con la scrittura di Main-Thread.
Thread 2: legge un campo, vedi sopra.


Ora supponiamo che Thread 1 scriva su un componente dell'array.

Thread 1: array[3] = 20;

Naturalmente dal punto di vista di Thread 1 non c'è nessuno problema. Se per caso Thread 1 dovesse successivamente leggere il valore di array[3] vedrà 20 perchè le azioni intra-thread - cioè le azioni compiute all'interno di uno stesso thread - hanno l'effetto che dovrebbero avere secondo l'ordine di apparizione nel codice.

Ma cosa succederebbe se in seguito Thread 2 leggesse il valore di array[3]? Abbiamo la garanzia che veda 20? O potrebbe vedere 10?

Per stabilire se sia certo che Thread 2 veda 20 dobbiamo poter rinvenire nel codice una relazione happens-before tra la scrittura:

Thread 1: array[3] = 20;

e la successiva ipotetica lettura:

Thread 2: print(array[3]);

Questa relazione tuttavia non c'è.

C'è una relazione tra ciò che accade prima di start nel Thread Main e ciò che accade dopo start in Thread 1 e Thread 2 ma non c'è tra ciò che capita in Thread 1 e 2 e quello che Main fa dopo averli avviati.

La mancanza di questa relazione ci fa dire che il programma non è correttamente sincronizzato e il risultato è indeterminato. Nella fattispecie sono legittime sia la lettura del valore 10, sia la lettura del valore 20.

Non è invece ammesso che array risulti null o che i suoi componenti valgano zero.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 21:44   #37
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Era quello che mi aspettavo infatti.
Quote:
Main-Thread: crea l'array e lo inizializza con dei valori. Questa inizializzazione comporta la scrittura di valori sull'heap
anche il Main, essendo di fatto un thread, deve rispondere al modello dell' area privata di lavoro(che a quanto ho capito è solo un qualcosa di teorico)? o girando la domanda se l' array fosse stato istanziando da un altro thread e non dal main, senza sincronizzazione sarebbe finito nella zona globale subito o nelle variabili private?
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 22:08   #38
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
I thread java sono tutti uguali. Il thread "main" non fa eccezione.

Tieni conto che in Java il metodo è l'unità di esecuzione dei Thread, cioè quando vedi un metodo sai che c'è un thread che lo sta eseguendo. Il metodo main è un metodo, quindi le istruzioni che contiene sono eseguite da un Thread.

la necessità di sincronizzare discende da questo fatto. Quando nel metodo main troviamo:

Codice:
public static void main(String[] args) {
   int[] array = {10, 10, 10, 10, 10};
Sappiamo che quella riga è eseguita da un certo Thread (la cui identità otterremmo invocando Thread.currentThread()).

Dopo c'è un:

new ProvaThread(array, 1).start();

quindi sappiamo che un Thread sicuramente diverso da quello che ha eseguito la prima riga farà qualcosa su array.

Ti confermo che il concetto dell'area privata di lavoro è solo metaforico: rende l'idea del perchè sarebbe possibile che un thread non veda un certo valore che nel codice sembra invece essere chiaramente determinato.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 22:36   #39
nuovoUtente86
Senior Member
 
Iscritto dal: Mar 2007
Messaggi: 7863
Ma l' effettiva sincronizzazione avviene appena prima di start(), indipendentemente che poi il thread operi o meno sull' array(starto un thread che stampa ciao e poi muore), oppure è legata al fatto che il thread sicuramente andrà ad operarci su?


Quote:
Ti confermo che il concetto dell'area privata di lavoro è solo metaforico: rende l'idea del perchè sarebbe possibile che un thread non veda un certo valore che nel codice sembra invece essere chiaramente determinato.
a livello pratico come è gestita la cosa dalla jvm?
nuovoUtente86 è offline   Rispondi citando il messaggio o parte di esso
Old 14-10-2009, 22:53   #40
PGI-Bis
Senior Member
 
L'Avatar di PGI-Bis
 
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
La sincronizzazione viaggia sempre in coppia: servono due azioni una compiuta da un Thread e una compiuta da un'altro Thread.

Ad esempio quando un Thread esce da un blocco synchronized, cioè che ha fatto fino ad allora è visibile al Thread che successicavamente entri in un blocco sincronizzato sullo stesso monitor.

Non c'è, per intenderci, una regola che dica: ok, dopo aver fatto questo il Thread aggiorna la metaforica regione di memoria condivisa. No, è sempre una questione di rapporto tra almeno due thread: dopo aver fatto questo, ciò che è stato fatto prima sarà visibile a qualcun'altro.

E' importante tenerlo presente altrimenti uno potrebbe pensare, come pare qualcuno faccia, che basti un punto di sincronizzazione non condiviso a rendere visibile l'operato di un thread:

Thread 1: scrivi, leggi, modifica e trallallà
Thread 1: synchronized(X) {} //PAF, il Thread "scarica" nell'HEAP

NO, non funziona così. Vale solo nei confronti di un secondo Thread per cui esista un vincolo hb col primo.

Quanto all'implementazione, non ho la più pallida idea di come sia fatta. Sì, in generale ci sono queste istruzioni nell'architettura di una CPU che sembrano essere in grado di stabilire se un valore debba essere preso presumo dalla cache piuttosto che dalla memoria principale. Poi si narra che ogni thread abbia la sua pila di "frame", uno per ogni metodo in esecuzione, che sono delle strutture dati che contengono una copia delle variabili chiamate in causa dall'esecuzione di un metodo.

Ma se vieni a chiedere dove sia allocata la memoria usata da questi frame per me potrebbe benissimo stare tra Nettuno e Plutone.

Comunque ci sono i sorgenti della JVM nel progetto OpenJDK. Una paio di volte ho anche provato a guardarli ma, francamente, più che capirci qualcosa direi di essere rimasto lì in affascinata contemplazione.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me!
PGI-Bis è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Mova Z70 Ultra Roller Complete: motore potente, rullo di lavaggio e l'IA a guidare Mova Z70 Ultra Roller Complete: motore potente, ...
Recensione Google Pixel 11: non ha l'HiLight dei Pro, ma è il Pixel più equilibrato di sempre Recensione Google Pixel 11: non ha l'HiLight dei...
Google Pixel 11 Pro XL: fotocamera al top, batteria indietro. Luci e ombre del nuovo flagship Google Pixel 11 Pro XL: fotocamera al top, batte...
Non sai programmare? Ecco cosa si può fare con un LLM e una GeForce RTX 5070 Ti Non sai programmare? Ecco cosa si può far...
Recensione Samsung Galaxy Z Fold8 Ultra: il pieghevole più famoso diventa quasi perfetto Recensione Samsung Galaxy Z Fold8 Ultra: il pieg...
LG lancia il primo monitor Full HD da 10...
Volvo Cars, nuovo CTO: arriva Alexander ...
Metro 2039 torna ad essere protagonista ...
Final Fantasy 7 Revelation si mostra in ...
Leapmotor taglia l’utile 2026 del 40%: r...
Apple Maps introduce ufficialmente la pu...
CD Projekt ha annunciato una versione re...
Tassa sui robot e lavori riservati agli ...
Revolut lancia EURR, la sua prima stable...
MacBook Lock riporta in vita i vecchi si...
Waymo sbarca a Monaco di Baviera: le cor...
NVIDIA NemoClaw, su Windows basta una pa...
Nokia Charge 300 è il nuovo dumbphone ch...
USB4 e capacità fino a 8 TB, ecco...
Tesla alza ancora il prezzo del Cybertru...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 14:40.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Served by www3v