In quali situazioni un blocco sincronizzato vuoto potrebbe ottenere la semantica di threading corretta?

StackOverflow https://stackoverflow.com/questions/686415

Domanda

Stavo guardando un Trovabug report sulla mia base di codice e uno dei modelli attivati ​​era per un file vuoto synchronzied blocco (cioè synchronized (var) {}).IL dice la documentazione:

I blocchi sincronizzati vuoti sono molto più sottili e difficili da usare correttamente di quanto la maggior parte delle persone riconosca e i blocchi sincronizzati vuoti non sono quasi mai una soluzione migliore delle soluzioni meno inventate.

Nel mio caso si è verificato perché il contenuto del blocco era stato commentato, ma il file synchronized la dichiarazione era ancora lì.In quali situazioni potrebbe essere vuoto synchronized blocco ottenere la semantica di threading corretta?

È stato utile?

Soluzione

Un blocco sincronizzato vuoto attenderà nessun altro sta usando che sincronizzatore. Questo può essere ciò che si vuole, ma perché non hanno protetto il codice successivo nel blocco sincronizzato, nulla è fermare qualcun altro di modificare ciò che mai era che stavate aspettando, mentre si esegue il codice successivo. Questo è quasi mai quello che vuoi.

Altri suggerimenti

Le risposte precedenti non riescono a sottolineare la cosa più utile sui blocchi synchronized vuoti: possono garantire la visibilità dei cambiamenti variabili e altre azioni in tutta thread. Come jtahlborn indica, sincronizzazione impone una "barriera memoria" dal compilatore che impedisce in filo e aggiornare la cache. Ma non ho trovato dove “Snake discute” questo, così ho scritto una risposta me stesso.

int variable;

void test() // This code is INCORRECT
{
    new Thread( () ->  // A
    {
        variable = 9;
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

Il programma di cui sopra non è corretto. Il valore della variabile può essere memorizzato nella cache localmente in filo A o B o entrambi. Quindi B potrebbe mai letto il valore di 9 che A scrive, e potrebbero quindi ciclo per sempre.

Fare un cambiamento variabile visibile attraverso fili utilizzando blocchi synchronized vuoti

Una possibile correzione è quello di aggiungere un volatile (in modo efficace "nessuna cache") modificatore alla variabile. A volte questo è inefficiente, però, perché vieta totalmente il caching della variabile. blocchi synchronized vuoti, d'altra parte, non vietano la memorizzazione nella cache. Non fanno altro che forzare le cache per la sincronizzazione con la memoria principale in alcuni punti critici. Ad esempio: *

int variable;

void test() // Corrected version
{
    new Thread( () ->  // A
    {
        variable = 9;
        synchronized( o ) {} // Flush to main memory
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            synchronized( o ) {} // Refresh from main memory
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

final Object o = new Object();

Come il modello di memoria garantisce visibilità

Entrambi i fili devono sincronizzare sullo stesso oggetto, al fine di garantire la visibilità. Questa garanzia si basa sul Java memoria di modello , in particolare la regola che "sbloccare azione sul monitor m sincronizza con- tutte le azioni serratura successivi riguardanti m" e quindi accade-prima quelli Azioni. Quindi sblocco di un monitor di o alla coda del suo blocco synchronized accade-prima successivo blocco di B alla testa del suo blocco. (Nota, è questo strano ordine della coda-testa della relazione che spiega perché i corpi possono essere vuote.) Inoltre, poiché scrittura di una precede suo sblocco e blocco di B precede la sua lettura, il rapporto deve estendersi a coprire sia scrittura e lettura: < em> scrittura accade-prima lettura . E 'questo cruciale, RelazioneEstesaAQuantitá che rende il programma rivisto corretto dal punto di vista del modello di memoria.

Credo che questo sia l'uso più importante per i blocchi synchronized vuoti.


* Io parlo come se fosse una questione di caching del processore perché penso che sia un modo utile di visualizzazione. In verità, come Aleksandr Dubinsky ha commentato, ‘tutti i processori moderni sono cache-coerente. Il accade-prima relazione è più su ciò che il compilatore è permesso di fare, piuttosto che la CPU ‘.

Ha usato essere il caso che la specifica verificato talune operazioni barriera memoria implicita. Tuttavia, la specifica ora è cambiato e le specifiche originali non è mai stato implementato correttamente. Può essere usato per attendere un altro filo per sbloccarlo, ma coordinato che l'altro thread ha già acquisito la serratura sarebbe difficile.

La sincronizzazione fa qualcosa di più che una semplice attesa, mentre una codifica poco elegante potrebbe ottenere l'effetto richiesto.

Da http://www.javaperformancetuning.com/news/qotm030.shtml

  1. Il thread acquisisce il blocco sul monitor per oggetto questo (presupponendo che il monitor sia sbloccato, altrimenti il ​​thread attende finché il monitor non viene sbloccato).
  2. La memoria del thread scarica tutte le sue variabili, vale a direha tutte le sue variabili effettivamente lette dalla memoria "principale" (le JVM possono utilizzare set sporchi per ottimizzarlo in modo che solo le variabili "sporche" vengano scaricate, ma concettualmente è lo stesso.Vedere la sezione 17.9 delle specifiche del linguaggio Java).
  3. Il blocco di codice viene eseguito (in questo caso impostando il valore restituito al valore corrente di i3, che potrebbe essere appena stato resettato dalla memoria "principale").
  4. (Qualsiasi modifica alle variabili verrebbe normalmente ora scritta nella memoria "principale", ma per geti3() non abbiamo modifiche.)
  5. Il thread rilascia il blocco sul monitor per obiettare questo.

Per uno sguardo in profondità nel modello di memoria di Java, uno sguardo a questo video da 'argomenti avanzati nei linguaggi di programmazione' di Google serie: http://www.youtube.com/watch?v=1FX4zco0ziY

Si dà davvero una bella panoramica di ciò che il compilatore può (spesso in teoria, ma a volte, in pratica) fare per il vostro codice. roba essenziale per qualsiasi programmatore Java serio!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top