Perché Java non vede il valore aggiornato da un altro thread?

Si prega di guardare questo codice (tratto dal libro di Java efficace)

import java.util.concurrent.TimeUnit; public class Main { private static boolean stopReq; public static void main(String[] args) throws InterruptedException { Thread bgw = new Thread(new Runnable() { public void run(){ int i = 0; while(!stopReq){ i++;} } }); bgw.start(); TimeUnit.SECONDS.sleep(1); stopReq = true; } } 

Perché il thread bgw si blocca in un loop infinito? È il caching della propria copia di stopReq quando ha raggiunto il ciclo? Quindi non vede mai il valore aggiornato dall’altro thread?

Capisco che la soluzione a questo problema sarebbe la sincronizzazione o una variabile volatile, ma sono curioso di sapere perché questa attuale implementazione non funziona.

Grazie

La tua spiegazione è giusta .

Il compilatore rileva che stopReq non viene mai modificato nel ciclo e poiché non è volatile , ottimizza l’istruzione while(!stopReq) a while(true) .

Anche se il valore cambia in seguito, il thread non lo legge nemmeno più.

Si dovrebbe leggere di più su Java Memory Model per comprendere meglio tutte le implicazioni.

In breve tempo, la variabile stopReq non è volatile o inclusa in un blocco sincronizzato offre alla VM la libertà di utilizzare una memoria locale ottimizzata (ad esempio registri, ecc.) Che non è garantita per propagare le modifiche immediatamente attraverso i thread.

Quando si dichiara la variabile come volatile, la VM si accerterà che dopo ogni variabile di scrittura venga inserita una “barriera di scrittura della memoria” che forzerà tutte le modifiche locali a essere riversate nella posizione di memoria reale rendendola così visibile a tutti gli altri thread ( la stessa barriera è posizionata alla fine di un blocco sincronizzato es.)

Imposta stopReq su true, quindi verrà arrestato. Stai di nuovo impostando stopReq su false, a causa del fatto che la condizione del ciclo while è sempre true ed è in loop infinito.

Ho provato questo, e no, le variabili sono le stesse. L’esempio si compila anche per me.

L’errore è qui:

Il tuo ciclo while continua, finché! StopReq è vero, ciò significa che stopReq è falso. E dopo 1 secondo si imposta stopReq su false – questo non cambia nulla. Se lo si imposta su true,! StopReq diventerà falso e il ciclo terminerà.

Per essere molto specifici sulla tua query, per sfruttare appieno le prestazioni del moderno hardware multiprocessore, in assenza di sincronizzazione, le JVM hanno permesso di consentire al compilatore di riordinare le operazioni e i valori di cache nei registri e nelle cache specifiche del processore. Poiché il thread principale scrive su stopReq senza sincronizzazione, a causa del riordino e della memorizzazione nella cache, il thread BTW potrebbe non visualizzare mai il valore scritto e il ciclo per sempre.

Quando si utilizza la sincronizzazione o la volatilità, garantiscono VISIBILITÀ e impongono al compilatore di non memorizzare nella cache e di scaricare le modifiche nella memoria principale.