2013-02-27 15 views
5

Questo article parla della parola chiave "sincronizzata" di Java.Thread Java "sincronizzati"

... 
    private int foo; 
    public synchronized int getFoo() { return foo; } 
    public synchronized void setFoo(int f) { foo = f; } 

Se un chiamante vuole incrementare la proprietà foo, il seguente codice per farlo non è thread-safe:

... 
    setFoo(getFoo() + 1); 

Se due thread tentano di incrementare foo, allo stesso tempo, il il risultato potrebbe essere che il valore di foo viene aumentato di uno o due, a seconda del tempo.

Ora, la mia domanda:

Perché non "sincronizzato", a setFoo() impedire che quanto sopra in grassetto linea?

risposta

6

Questo è un esempio di una condizione di gara check-poi-atto.

Uno scenario potrebbe accadere come la seguente:

Thread-1 getFoo() returns 0 
Thread-2 getFoo() returns 0 
Thread-2 setFoo(1) 
Thread-1 setFoo(1) 

Ciò significa che due thread hanno tentato di incrementare foo ma ha l'effetto di un solo essere incrementato una volta.

Come altre risposte hanno identificato, la sincronizzazione dell'incremento con un blocco di blocco sincronizzato sullo stesso oggetto di getFoo() e setFoo() impedirà questa condizione di competizione perché i thread non saranno in grado di interlacciare come sopra.

6

perché tu sei garantito che nessun altro sta ottenendo foo al vostro fianco, e che nessun altro è la creazione di pippo indietro oltre a te, ma tu sei non garantita nessuno è riuscito a entrare e uscire (o solo in) tra ti chiama get() e ti chiama set()

si può pensare che il codice come del tutto equivalente a questo:

int temp = getFoo(); //safe method 
temp = temp+1; //not protected here - im not holding any locks ... 
setFoo(temp); //safe method 
4

La synchronized parola chiave su entrambi i metodi non lo rende thread-safe, perché un thread potrebbe chiama getFoo, quindi un altro thread potrebbe chiamare getFoo e ognuno di essi ottiene lo stesso risultato. Quindi ciascuno di essi ne aggiunge uno e chiama lo setFoo e il risultato finale è che foo viene incrementato una sola volta, anziché due. Come indicato dal tuo articolo, questa è una condizione di gara .

Per renderlo thread-safe, sia la lettura che la scrittura devono essere insieme nello stesso blocco sincronizzato, senza metodi get e set separati.

public synchronized void addFoo(int addend) 
{ 
    foo += addend; 
} 
+0

'Quindi ognuno di essi aggiunge uno e chiama setFoo, e il risultato finale è che foo viene incrementato solo una volta, invece di due volte' Perché? –

+0

Poiché ogni thread aggiorna 'foo' sullo stesso valore. Ad esempio, ognuno di essi ottiene il valore 2, ognuno di essi aggiunge 1 per ottenere 3, quindi ognuno di essi imposta il valore su 3. – rgettman

+0

Se ho capito bene, la frase non dovrebbe essere "Quindi ognuno di essi aggiunge uno" e chiama setFoo, e il risultato finale è che foo viene incrementato ** due volte **? ' –

1

La trappola principale del codice è che potrebbe sembrare che getFoo venga chiamato "interno" setFoo. Tipo di

setFoo(){ 
    //getFoo(); 
    //... 
} 

che non è corretto perché in realtà getFoo è chiamato prima di chiamare setFoo.Ecco esempio che mostra che:

public static int foo(int i) { 
    System.out.print("FOO!"); 
    return i; 
} 

public static int bar(int i) { 
    System.out.print("BAR!"); 
    return i; 
} 

public static void main(String[] args) throws Exception { 
    System.out.println(foo(bar(1))); 
} 

uscita:

BAR!FOO!1 

Come si può vedere bar è stato invocato prima foo. Quindi nel tuo caso è possibile che due (o più) thread invocheranno getFoo che restituiranno il valore corrente prima che chiamino setFoo. In questo caso saranno entrambi hanno lo stesso valore, consente di dire 0 e quando chiameranno setFoo saranno entrambi impostarlo su 1.

0

fa questo codice di aiuto?

class C { 
    private int foo; 
    public int getFoo() { return foo; } 
    public void setFoo(int f) { foo = f; } 
} 

C myC = new C(); 
synchronized(myC) { 
    int foo = myC.getFoo(); 
    myC.setFoo(foo + 1); 
} 
println(myC.foo); 
+0

Quindi il punto è che' myC.setFoo (1) 'potrebbe essere chiamato prima di' println (myC.getFoo()) '? –