2013-04-27 13 views
31

Durante la lettura di documenti Java su errori di coerenza della memoria. Trovo punti relativi alle due azioni che crea accadere - prima relazione:Coerenza della memoria - relazione di prima esecuzione in Java

  • Quando un'istruzione invoca Thread.start(), ogni affermazione che ha un accade-prima relazione con questa affermazione ha anche un accade-prima relazione con ogni dichiarazione eseguita dal nuovo thread . Gli effetti del codice che ha portato alla creazione del nuovo thread sono visibili per il nuovo thread.

  • Quando un thread termina e provoca un Thread.join() in un altro thread rinviare, quindi tutte le istruzioni eseguite dal terminato
    filo hanno accade-prima relazione con tutte le dichiarazioni
    seguenti join successo. Gli effetti del codice nella discussione sono ora visibili al thread che ha eseguito il join.

Non riesco a capire il loro significato. Sarebbe bello se qualcuno lo spiegasse con un semplice esempio.

+5

"succursale prima" significa che tali serie di istruzioni sono garantite per l'esecuzione prima di un'altra serie di istruzioni. Quindi in 1 ° scenario .. le istruzioni che portano all'apertura di un nuovo thread hanno una relazione prima-accade con le istruzioni che verranno eseguite dal thread appena avviato. Qualsiasi modifica apportata da tali istruzioni sarà visibile alle istruzioni eseguite dal thread. –

+1

Trovo utile questa pagina: http://preshing.com/20130702/the-happens-before-relation/ Fornisce esempi di come una relazione "succede prima" tra A e B sia diversa da A che sta effettivamente accadendo prima di B. –

risposta

26

Le CPU moderne non sempre scrivono i dati in memoria nell'ordine in cui sono stati aggiornati, ad esempio se si esegue lo pseudo codice (assumendo che le variabili siano sempre memorizzate nella memoria qui per semplicità);

a = 1 
b = a + 1 

... la CPU può benissimo scrivere b alla memoria prima che scrive a alla memoria. Questo non è un problema purché si eseguano le cose in un singolo thread, poiché il thread che esegue il codice sopra non vedrà mai il vecchio valore di entrambe le variabili una volta che i compiti sono stati fatti.

Il threading multiplo è un'altra cosa, si potrebbe pensare che il seguente codice consentirebbe ad un altro thread di raccogliere il valore del proprio calcolo pesante;

a = heavy_computation() 
b = DONE 

... l'altro thread facendo ...

repeat while b != DONE 
    nothing 

result = a 

Il problema però è che la bandiera fatto può essere impostato in memoria prima che il risultato viene memorizzato nella memoria, in modo da l'altro thread può raccogliere il valore dell'indirizzo di memoria a prima che il risultato del calcolo sia scritto in memoria.

Lo stesso problema sarebbe - se Thread.start e Thread.join non ha un "accade prima" garanzia - dare problemi con il codice simili;

a = 1 
Thread.start newthread 
... 

newthread: 
    do_computation(a) 

... poiché a non può avere un valore memorizzato nella memoria quando il filo si avvia.

Dal momento che si vuole quasi sempre il nuovo thread per essere in grado di utilizzare i dati è stata inizializzata prima di iniziarlo, Thread.start ha un "avviene prima" garanzia, vale a dire, i dati che è stato aggiornato prima di chiamare Thread.start è garantito per essere disponibili alla nuova discussione.La stessa cosa vale per Thread.join in cui è garantito che i dati scritti dal nuovo thread siano visibili al thread che lo unisce dopo la terminazione.

Semplifica la filettatura.

7

problemi di visibilità filettatura possono verificarsi in un codice che non è sincronizzata correttamente secondo il modello di memoria Java. A causa delle ottimizzazioni hardware del compilatore &, le scritture di un thread non sono sempre visibili dalle letture di un altro thread. Il modello di memoria Java è un modello formale che rende chiare le regole di "correttamente sincronizzate", in modo che i programmatori possano evitare problemi di visibilità del thread.

Happens-before è una relazione definita in tale modello e fa riferimento a esecuzioni specifiche. Una scrittura W dimostrata come avviene prima di una lettura R è garantita per essere visibile da quella lettura, presupponendo che non ci siano altre scritture interferenti (cioè una relazione senza avvenimenti prima della lettura o una che accade tra di loro secondo quella relazione).

Il tipo più semplice di succede - prima che la relazione avvenga tra azioni nella stessa discussione. Una scrittura da W a V nel thread P avviene - prima di una lettura R di V nello stesso thread, assumendo che W venga prima di R secondo l'ordine del programma.

Il testo a cui si fa riferimento afferma che thread.start() e thread.join() garantiscono anche la relazione prima-evento. Qualsiasi azione che si verifica - prima che thread.start() avvenga anche prima di qualsiasi azione all'interno di quel thread. Allo stesso modo, le azioni all'interno del thread avvengono prima delle azioni che appaiono dopo thread.join().

Qual è il significato pratico di ciò? Se ad esempio si avvia un thread e si attende che termini in modo non sicuro (ad esempio un sleep per un lungo periodo di tempo o test di un flag non sincronizzato), quindi quando si proverà a leggere le modifiche dei dati eseguite dal thread, potresti vederli parzialmente, con il rischio di incoerenze nei dati. Il metodo join() funge da barriera che garantisce che qualsiasi porzione di dati pubblicata dal thread sia visibile completamente e in modo coerente dall'altro thread.

18

Considerate questo:

static int x = 0; 

public static void main(String[] args) { 
    x = 1; 
    Thread t = new Thread() { 
     public void run() { 
      int y = x; 
     }; 
    }; 
    t.start(); 
} 

Il filo conduttore è cambiato campo x. Il modello di memoria Java non garantisce che questa modifica sarà visibile ad altri thread se non sono sincronizzati con il thread principale. Ma filo t vedrà questo cambiamento, perché il thread principale chiama t.start() e JLS garanzie che chiamare t.start() rende la modifica x visibile in t.run() così y è garantita da assegnare 1.

Le stesse preoccupazioni Thread.join();

+0

Beh, sono d'accordo, ho cambiato la risposta un po 'per evitare critiche, date un'occhiata ora –

1

Secondo il documento oracolo, essi definiscono la relazione che The accade-prima è semplicemente una garanzia che memoria scrive da una specifica dichiarazione sono visibili all'altro dichiarazione specifica.

package happen.before; 

public class HappenBeforeRelationship { 


    private static int counter = 0; 

    private static void threadPrintMessage(String msg){ 
     System.out.printf("[Thread %s] %s\n", Thread.currentThread().getName(), msg); 
    } 

    public static void main(String[] args) { 

     threadPrintMessage("Increase counter: " + ++counter); 
     Thread t = new Thread(new CounterRunnable()); 
     t.start(); 
     try { 
      t.join(); 
     } catch (InterruptedException e) { 
      threadPrintMessage("Counter is interrupted"); 
     } 
     threadPrintMessage("Finish count: " + counter); 
    } 

    private static class CounterRunnable implements Runnable { 

     @Override 
     public void run() { 
      threadPrintMessage("start count: " + counter); 
      counter++; 
      threadPrintMessage("stop count: " + counter); 
     } 

    } 
} 

uscita sarà:

[Thread main] Increase counter: 1 
[Thread Thread-0] start count: 1 
[Thread Thread-0] stop count: 2 
[Thread main] Finish count: 2 

avere un'uscita sguardo, linea [filo filo-0] inizio conteggio: 1 mostra che tutte le modifiche contatore prima invocazione Thread.start() sono visibile nel corpo di Thread.

E linea [Discussione principale] Fine conteggio: 2 indica che tutti i cambiamenti nel corpo di filettatura sono visibili a filo principale che chiama Thread.join().

Spero che possa aiutarti in modo chiaro.

Problemi correlati