Questo è chiamato effetto "pubblicazione prematura".
Semplicissimo, JVM può riordinare le istruzioni del programma (per motivi di prestazioni), se tale riordino non viola le restrizioni di JMM.
Si vede che il codice f = new FinalFieldExample();
a funzionare come:
1. istanziare FinalFieldExample
2. assegnare 3 x
3. assegnare 4 a y
4. assegnare oggetto creato alla variabile f
Ma in codice fornito, niente può fermare JVM dall'istruzione riordino, in modo che possa eseguire il codice come:
1. istanziare FinalFieldExample
2. assegnare 3 x
3. assegnare crudo, oggetto non inizializzato completamente alla variabile f
4. assegnare 4 a y
Se riordino avviene in un'unica thread environment, non lo noteremo nemmeno. Questo perché ci aspettiamo che gli oggetti vengano completamente creati prima di iniziare a lavorare con loro e JVM rispetti le nostre aspettative. Ora, cosa può succedere se diversi thread eseguono questo codice contemporaneamente? Nel seguente esempio Filettatura1 è metodo writer()
e Thread2 eseguendo - metodo reader()
:
filettatura 1: creare l'istanza di FinalFieldExample
filettatura 1: assegnare 3 x
filettatura 1: assegnare crudo, oggetto non inizializzato completamente alla variabile f
filettatura 2: lettura f
, non è nullo
filettatura 2: lettura fx, è 3
filettatura 2: lettura fy, è ancora 0
filettatura 1: assegnare 4 a y
Definitivamente non buono. Per evitare che JVM faccia ciò, dobbiamo dargli ulteriori informazioni sul programma. Per questo particolare esempio, ci sono alcuni modi per risolvere la consistenza memoria:
- dichiarare
y
come final
variabile. Ciò causerà l'effetto "freeze". In breve, le variabili finali saranno sempre inizializzate nel momento in cui le si accede, se il riferimento all'oggetto non è trapelato durante la costruzione.
- dichiarare
f
come volatile
variabile.Questo creerà "synchronization order" e risolverà il problema. In breve, le istruzioni non possono essere riordinate sotto la scrittura volatile e sopra la lettura volatile. L'assegnazione alla variabile f
è una scrittura volatile, ovvero le istruzioni new FinalFieldExample()
non possono essere riordinate ed eseguite dopo l'assegnazione. La lettura dalla variabile f
è una lettura volatile, pertanto la lettura di f.x
non può essere eseguita prima di essa. La combinazione di v-write e v-read è chiamata ordine di sincronizzazione e fornisce la coerenza di memoria desiderata.
Here è un buon blog, in grado di rispondere a tutte le vostre domande su JMM.
So che questo si riferisce alla JVM che è autorizzata a riordinare le scritture, ma non conosco i dettagli. : - | –
Siccome penso che non abbiate letto correttamente jsr133, ciò è chiaramente menzionato. Non scrivere un riferimento all'oggetto che si sta costruendo in un punto in cui un altro thread può vederlo prima che il costruttore dell'oggetto sia finito. Se questo è seguito, quando l'oggetto viene visto da un altro thread, quel thread vedrà sempre la versione correttamente costruita di i campi finali dell'oggetto, ma per i thread non finali il thread può vedere il valore predefinito. – Prashant
sì, questo è folle. speriamo che lo risolvano in java9. – ZhongYu