2011-10-03 7 views
16

Sto realizzando un programma in java che corre contro alcune macchine. Ogni auto è un thread separato.L'operatore di pre-incremento è thread-safe?

Quando le auto completano la gara, ognuna chiama questo metodo. Ho provato il metodo a diverse velocità del timer e sembra funzionare bene. Ma mi rendo conto che ogni thread sta accedendo alla variabile carsComplete, a volte alla stessa ora esatta (almeno nello scope che il comando date mi sta dando).

Quindi la mia domanda è: questo metodo è thread-safe?

public static String completeRace() 
{ 
     Date accessDate = new Date(); 
     System.out.println("Cars Complete: " + carsComplete + " Accessed at " + accessDate.toString()); 
     switch(++carsComplete) 
     { 
      case 1: return "1st"; 
      case 2: return "2nd"; 
      case 3: return "3rd"; 
      default: return carsComplete + "th";  
     } 
} 

risposta

19

No, dovresti usare qualcosa come java.util.concurrent.atomic.AtomicInteger. Guarda il suo metodo getAndIncrement().

+3

Oppure sincronizzare qualcosa prima di incrementare. –

+1

@Ted: Scommetto che la sincronizzazione su qualcosa è più costosa. –

+1

@Eric - Sarei sorpreso se AtomicInteger non eseguisse alcuna sincronizzazione ad un certo punto (forse nel codice nativo). Sarebbe interessante per benchmark. Dalla sorgente di AtomicInteger, sembra dipendere da un algoritmo di confronto e impostazione atomico che tenta fino a quando non rileva alcun problema. Non c'è nessun limite peggiore per questo; in scenari altamente filettati, penso che potrebbe essere molto lento a volte casuali. –

5

++ L'operatore non è atomico. Guarda qui http://madbean.com/2003/mb2003-44/. Per le operazioni atomiche è possibile utilizzare AtomicInteger

AtomicInteger atomicInteger = new java.util.concurrent.atomic.AtomicInteger(0) 

e ogni volta che si desidera incrementare si può chiamare atomicInteger.incrementAndGet() metodo che restituisce un int primitiva. 0 è il valore iniziale predefinito per il numero intero atomico.

+0

non riesco ancora a capire perché le operazioni atomiche sono sicure? cosa succede quando due thread eseguono CAS allo stesso tempo ?? non è una condizione di gara ?? – hardik

+1

@Hardik non esiste alcuna condizione di competizione perché la relazione 'accade prima' (http://en.wikipedia.org/wiki/Happened-before) viene applicata dalle operazioni atomiche e quindi dalla sicurezza del thread risultante. –

8

pre-incremento su int è non filo sicuro, usare AtomicInteger che-lock gratis:

AtomicInteger carsComplete = new AtomicInteger(); 

//... 

switch(carsComplete.incrementAndGet()) 

proposito il codice sotto è non sicuro filo pure. Puoi dire perché?

carsComplete.incrementAndGet(); 
switch(carsComplete.get()) 
+1

Poiché il thread 1 può raggiungere l'istruzione switch, resa a thread 2, che esegue incrementAndGet incrementando così il backing store per carsComplete, a quel punto il thread 1 viene eseguito nuovamente con il backing store due volte, una per ogni thread. –

+0

Esattamente, ma senza premio questa volta ;-). –

5

Stesso come in C++ l'operatore ++ non è atomica.

In realtà è più di 1 istruzioni in esecuzione sotto il cofano (non fatevi ingannare dal vedere un semplice ++i, è load/add/store) e poiché non v'è più di 1 istruzione coinvolti senza sincronizzazione si possono avere varie interleavings con risultati sbagliati.

Se è necessario incrent il carsComplete in un modo thread-safe è possibile utilizzare costrutto di Java AtomicInteger oppure si potrebbe sincronizzare l'intero metodo

1

domanda è “è sicuro pre incremento filo operatore?”

Ans : No perchè ? a causa del numero di istruzioni coinvolte. Atomico significa singola operazione, qui devono essere eseguite le operazioni di caricamento/aggiunta/memorizzazione. Quindi non un'operazione atomica.

Same for post increment. 
Problemi correlati