2013-06-06 16 views
9

Ho difficoltà a comprendere le variabili volatili in Java.Le variabili volatili richiedono l'accesso sincronizzato?

ho una classe parametri che contiene una variabile volatile, in questo modo:

public class MyClass<T> { 

    private volatile T lastValue; 

    // ... other code ... 

} 

devo implementare alcune operazioni di base contro lastValue compreso un get-valore-se-non-nullo.

Queste operazioni devono essere sincronizzate? Posso farla franca con il seguente metodo?

public void doSomething() { 
    String someString; 
    ... 
    if (lastValue != null) { 
     someString += lastValue.toString(); 
    } 
} 

Oppure è necessario incollare il controllo Null in un blocco sincronizzato?

public void doSomething() { 
    String someString; 
    ... 

    synchronized(this) { 
     if (lastValue != null) { 
      someString += lastValue.toString(); 
     } 
    } 
} 

So che per operazioni atomiche come ottenere e set, dovrei essere ok senza applicare la sincronizzazione (ad es. public T getValue() { return lastValue; }). Ma non ero sicuro delle operazioni non atomiche.

+0

riscrittura 'someString + = lastValue.toStrin();' come 'someString + = ("" + lastValue);' consente di sbarazzarsi del 'null' controllare tutto. – dasblinkenlight

+4

@dasblinkenlight, true, ma solo se si sta bene con la parola "null" che appare nella stringa risultante quando lastValue è nullo. –

+0

@IanMcLaird Ah, hai ragione, ho dimenticato che Java lo fa (è .NET che non lo fa). – dasblinkenlight

risposta

13

volatile garantisce la visibilità (le modifiche apportate da un thread verranno visualizzate da altri thread) ma non garantisce l'atomicità di più operazioni.

Quindi sì, lastValue potrebbe diventare null tra if (lastValue != null) e someString += lastValue.toString(); e il vostro codice potrebbe gettare un NullPointerException.

È possibile aggiungere la sincronizzazione (ma è necessario sincronizzare tutte accessi in scrittura alla variabile), o per quella semplice caso d'uso, è possibile utilizzare una variabile locale:

public void doSomething() { 
    String someString; 
    T lastValueCopy = lastValue; 
    if (lastValueCopy != null) { 
     someString += lastValueCopy.toString(); 
    } 
} 
+0

Perché l'aggiunta della sincronizzazione al controllo Null richiede l'aggiunta della sincronizzazione alle operazioni atomiche rispetto alla variabile volatile? –

+0

ho capito correttamente che non esiste alcuna nozione di "prima" e "dopo" per i thread eccetto quando vengono sincronizzati ad un certo punto nel tempo? La semantica del thread consente a un thread di visualizzare un vecchio valore di variabile dopo che un altro thread lo ha aggiornato? – kutschkem

+0

@RoddyoftheFrozenPeas Hai ragione, hai solo bisogno di sincronizzare tutte le operazioni di scrittura. Ciò è necessario perché qui viene utilizzato 'synchronized' per l'esclusione reciproca, ovvero assicurandosi che nessun thread modifichi la variabile tra queste due righe. Questo può funzionare solo se tutti i thread che cercano di scrivere sulla variabile devono acquisire quel blocco. – assylias

2

Questo dipende pesantemente sul proprietà del tipo T. Nel caso più semplice, T è un tipo immutabile, cioè qualsiasi valore di T che leggi, non cambierà il suo stato interno in seguito. In questo caso non è necessaria la sincronizzazione, basta leggere il riferimento in una variabile locale e lavorare con esso fino a quando è necessario.

Se T è un tipo mutabile, è necessario proteggersi dall'istanza cambiandone lo stato mentre si lavora con esso. In questo caso è necessaria la sincronizzazione che in particolare garantisce che l'istanza di T sia protetta da esso. La sincronizzazione su questo in genere non garantisce che, non ti impedisca di alterare lo stato di T da qualsiasi altro luogo. Il blocco appropriato per una specifica T deve essere definito dalle regole (e non esiste un elemento linguistico che garantisca che non si infrangi quelle regole).

Nel caso speciale T è mutabile e hai definito il tuo questo come il blocco corretto, hai bisogno di sincronizzazione, ma non hai più bisogno di volatile.

Detto questo l'uso di volatile in combinazione con l'aspetto sincronizzato è sospetto.

+0

@loki Non necessariamente, ma quando diventa visibile sarà in uno stato coerente. Se l'oggetto non è immutabile, quando diventa visibile, il thread potrebbe vedere l'oggetto con alcuni campi correttamente costruiti e alcuni campi che hanno il loro valore predefinito (false, 0, null). – assylias

Problemi correlati