2013-03-05 11 views
7

Diciamo che ho la seguente classe che verrà letta pesantemente, ma solo scritta occasionalmente. Sarà usato in una web app multi-threaded, quindi ha bisogno di essere thread-safe:Sincronizza accesso in scrittura al campo Volatile (blocco di lettura/scrittura economico)

public class Foo { 
    private volatile String foo; 
    public String getFoo() { 
     return foo; 
    } 
    public synchronized String setFoo(String in) { 
     this.foo = in; 
    } 
} 

Java Concurrency (http://www.ibm.com/developerworks/java/library/j-jtp06197/index.html) afferma che questo è un modo fragile per proteggere l'accesso in scrittura, migliorando l'accesso in lettura. Qual è un'alternativa più forte a questo modello? O qualsiasi alternativa se foo avrà bisogno di essere trasformato in un ambiente pesante da leggere? Grazie.

risposta

14

Volatile fornisce rapido accesso senza blocchi thread-safe a un campo senza sincronizzazione

private volatile String foo; 

public String getFoo() { 
    return foo; 
} 
public void setFoo(String in) { 
    this.foo = in; 
} 

risolve volatili 3 problemi 1) visibilità memoria 2) scrive atomici per campi doppie e lunghe 3) vieta istruzioni riordino. Ma non è abbastanza se hai bisogno di più operazioni su un campo come una transazione atomica, come l'incremento. Questo codice è rotto

private volatile int id; 

public void incrementId() { 
    id++; 
} 

perché se 2 fili simulataneously leggere e incrementarlo e per salvare il risultato allora il risultato del primo incremento viene sovrascritto con il risultato del secondo incremento. Per evitare che ciò accada abbiamo bisogno di usare la sincronizzazione

private int id; 

public synchronized int nextId() { 
     return ++id; 
} 

o un pacchetto java.util.concurrent.atomic

private AtomicInteger id = new AtomicInteger(); 

public void incrementId() { 
    return id.incrementAndGet(); 
} 
+1

Contrassegnare una variabile come volatile non lo rende thread-safe. Se tutto ciò che l'OP sta facendo è la lettura/scrittura, allora in effetti è thread-safe (questo non sarebbe vero se più thread stavano incrementando la variabile di supporto ...). –

+0

Quindi c'è qualche situazione in cui sia volatile che sincronizzato sarebbe appropriato? – oberger

+2

Sono sicuro che non esiste una situazione del genere. Se si sincronizza l'accesso a un campo, volatile è ridondante. –

2

Se tutti si sta facendo è l'impostazione foo, allora non è necessario sincronizzare il metodo. rendere il riferimento volatile è sufficiente.

+0

E se faccio di più nel metodo setter? Come operazione di copia o operazione di sottostringa. Vorrei solo sincronizzare o utilizzare entrambi? – oberger

+0

qualsiasi altra azione nel setter che ha bisogno di atomicità rispetto al valore della stringa richiederà l'uso della sincronizzazione, che renderà inutile il modificatore volatile –

+1

@OwenBerger - se hai bisogno di fare più lavoro nel setter, allora sì, dovresti necessità di sincronizzare. tuttavia, volatile è ancora utile in tale scenario in quanto consente di non sincronizzare il getter (presumendo che non ti interessi se un chiamante al getter ottiene un vecchio valore mentre un altro chiamante è nel setter). – jtahlborn

2

Al link hai detto c'è questo codice per "aggiornamenti poco frequenti" Utilizzo:

@ThreadSafe 
public class CheesyCounter { 
    // Employs the cheap read-write lock trick 
    // All mutative operations MUST be done with the 'this' lock held 
    @GuardedBy("this") private volatile int value; 

    public int getValue() { return value; } 

    public synchronized int increment() { 
     return value++; 
    } 
} 

Il metodo increment è solo utilizzando sincronizzato perché sta facendo più di un semplice impostare il valore della value come indicato nel descrizione, se tutto quello che stai facendo è this.foo = in; che è atomico. Nel testo la "fragilità di questo schema" indica che le cose possono diventare molto veloci quando si mescolano metodi volatili e altri metodi di sincronizzazione per fare qualcosa di più che semplici esempi. Vedi java.util.concurrent.locks pacchetti per le interfacce Condizione e blocco e la classe ReentrantLock. Penso che, e usando sincronizzato sia ciò che l'autore intende per "alternative più forti". Dovresti anche vedere Object.wait, Object.notify e Object.notifyAll se non lo sapevi ancora.

Problemi correlati