2010-06-02 14 views
7

Come possiamo usare AtomicInteger per la generazione di sequenze limitate dire che il numero di sequenza deve essere compreso tra 1 e 60. Una volta che la sequenza raggiunge 60 deve ricominciare da 1. Ho scritto questo codice anche se non sono sicuro se questo è thread sicuro o no?AtomicInteger per generazione sequenziale limitata

public int getNextValue() 
{ 
int v; 
do 
{ 
    v = val.get(); 
    if (v == 60) 
    { 
    val.set(1); 
    } 
} 
    while (!val.compareAndSet(v , v + 1)); 
    return v + 1; 
    } 

risposta

14

Si potrebbe fare

return val.getAndIncrement() % 60; 

meno che non siete interessati con il superamento del max-valore intero (2147483647). Se questo è un problema, si potrebbe avere uno sguardo alla getAndIncrement realizzazione:

public final int getAndIncrement() { 
    for (;;) { 
     int current = get(); 
     int next = current + 1; 
     if (compareAndSet(current, next)) 
      return current; 
    } 
} 

Tutto quello che devi cambiare è la linea int next... a qualcosa di simile:

int next = (current + 1) % 60; 

Ops. Questo scorre attraverso 0-> 59. Avevi bisogno di 1> 60, quindi aggiungi uno al valore di ritorno per ottenere il risultato desiderato.

+0

+1. È davvero utile – satish

+0

Se trovi una risposta davvero utile, fai clic sul segno di spunta per accettarla. – naiad

+1

Che strana implementazione. Fondamentalmente dice "aggiungi 1 se non è ancora cambiato, altrimenti continua a provare". Non potrebbe teoricamente produrre un ciclo infinito? –

0

No, non è thread-safe - non si dovrebbe chiamare set all'interno di un ciclo:

int value, next; 
do { 
    value = val.get(); 
    next = (value == 60) ? 1 : (value + 1); 
} while (!val.compareAndSet(value, next); 
return next; 
1

Se si effettua il metodo synchronized allora sarà threadsafe fino a quando il val è nessun altro luogo accessibile. L'approccio è però un po 'ingombrante, mi piacerebbe riscrivere come segue:

public synchronized int getNextValue() { 
    val.compareAndSet(60, 0); // Set to 0 if current value is 60. 
    return val.incrementAndGet(); 
} 

Questo dà 1 fino a 60 di nuovo inclusiva. Se è effettivamente necessario 1 fino a 59, quindi sostituire 60 per 59.

0

Un motivo particolare per utilizzare AtomicInteger qui piuttosto che un semplice metodo sincronizzato?

Che ne dite di qualcosa di semplice come il seguente:

private int val=1; 

public synchronized int getNextValue() { 
int v=val; 
val = (val==60) ? 1 : (val+1); 
return v; 
} 
+0

p.s. nulla contro AtomicIntegers, ma penso che sia sempre bene rendere le cose il più semplici possibile nell'interesse della manutenibilità a lungo termine .... – mikera

+0

Grazie per la risposta. Stavo cercando un algoritmo non bloccante instante l'algoritmo di blocco. – satish

+0

AtomicInteger è molto più veloce della sincronizzazione se c'è un sacco di contesa (fino a un fattore di 10). – starblue

0

risposta rapida, non è thread-safe. Il test e il set devono essere atomici, a meno che non si esegua la sincronizzazione dell'intero metodo. Si noti che val.get() e il test di v non sono atomici. Se il thread restituisce dopo v = val.get() otterrai due chiamate con lo stesso numero di sequenza.

Inoltre, se il confrontoAndSet ha esito negativo non si cambiano mai i valori, sarà un ciclo infinito.

AtomicInteger ha una chiamata getAndIncrement() . Questo ti darà un valore pulito da restituire.

Il rolling è un po 'più complicato. Una soluzione è modificare il valore di ritorno. Qualcosa del genere:

int v = val.getAndIncrement(); 
return (v % 60) + 1; 

Poiché ogni thread ha una copia locale di v possiamo tranquillamente fare alcuni calcoli e restituire il valore. C'è un punto critico se si ottiene un overflow. A seconda della frequenza con cui si genera un numero di sequenza, questo può o non può essere un problema.

+0

La frequenza della generazione è piuttosto alta, probabilmente ogni 50 ms. – satish

+0

A 20 chiamate al secondo (una chiamata ogni 50ms) si otterrebbe un rollover dopo circa 3,4 anni. Probabilmente non troppo male, a seconda della tua app. Se questo sarà un problema, un blocco sincronizzato è probabilmente la soluzione più semplice. Assicurati che questo codice sia un vero e proprio collo di bottiglia prima di impazzire ottimizzandolo. Sospetto che ci siano frutti pendenti più bassi di un compito eseguito ogni 50ms. –

3

È possibile eseguire questa operazione in una singola riga utilizzando Java 8.

AtomicInteger counter = new AtomicInteger(); 

public int getNextValue() { 
    return counter.updateAndGet(n -> (n >= 60) ? 1 : n + 1); 
} 
Problemi correlati