2010-03-15 17 views
17

Ho una necessità di un contatore di tipo long con i seguenti requisiti/fatti:Long primitive o AtomicLong per un contatore?

  • incrementare il contatore dovrebbe prendere il minor tempo possibile.
  • Il contatore verrà scritto solo da un thread.
  • La lettura dal contatore verrà eseguita in un altro thread.
  • Il contatore verrà incrementato regolarmente (fino a qualche migliaio di volte al secondo), ma verrà letto solo una volta ogni cinque secondi.
  • Precisione di precisione non è essenziale, solo un'idea approssimativa delle dimensioni del contatore è abbastanza buona.
  • Il contatore non viene mai cancellato, decrementato.

Sulla base di questi requisiti, come sceglieresti di implementare il tuo contatore? Come semplice long, come volatile long o con un AtomicLong? Perché?

Al momento ho un volatile long ma mi chiedevo se un altro approccio sarebbe stato meglio. Sto anche incrementando il mio lungo facendo ++counter in contrasto con counter++. E 'davvero più efficiente (come sono stato portato a credere altrove) perché non è stato fatto nessun incarico?

Grazie in anticipo

Rich

+0

volatile dovrebbe andare bene, poiché il contratto stabilisce che ogni lettura su una variabile volatile viene dopo il rilascio del blocco di una scrittura precedente. – fasseg

+0

Se lo aggiorni solo poche migliaia di volte al secondo e usi un processore non incorporato, la differenza è irrilevante; puoi fare milioni di aggiornamenti "AtomicLong' al secondo su quasi tutte le macchine in questi giorni. Tuttavia, sono d'accordo che "volatile" dovrebbe essere sufficiente. Potresti codificarli entrambi contemporaneamente e controllare quanto spesso corrispondono (e quando non sono lontani) se vuoi essere sicuro. –

+0

@Riduidel contiamo i pacchetti in entrata di dati, e non ci importa se il numero è 12,345,678 o se è leggermente in ritardo a 12,345,602 – Rich

risposta

11

Attribuite questi set di requisiti, ho pensano che un volatile lungo dovrebbe essere sufficiente. Il contatore non sarebbe errato con una lunghezza non volatile, ma il lettore potrebbe leggere le informazioni non aggiornate in quel caso.

Un problema è che legge e scrive un long sono not required to be atomic, dal JVM specification se non è dichiarato volatile. Ciò significherebbe che il thread di lettura potrebbe ottenere un valore fittizio se legge il valore mentre il thread di scrittura ha aggiornato una parte del valore, ma non l'altro.

La differenza tra ++counter e counter++ è probabilmente irrilevante, come JVM si renderà conto che il valore dell'espressione non viene più utilizzata ei due sono equivalenti in questo caso.

0

qual è il requisito di disponibilità per il programma? Potresti accontentarti di un int non volatile e di una lettura vivace?

+0

Uptime è il più lungo possibile - questo è in esecuzione all'interno di un server. – Rich

0

10^4 incrementi/secondo è 1 ogni 100 usec. L'efficienza non è un problema, ma l'atomicità potrebbe essere. Potresti averne 2 copie e, quando viene letta, se non sono uguali, leggi di nuovo.

0

Questo article parla dei possibili modi per implementare un contatore Credo che questa implementazione dovrebbe funzionare per voi

class LessNaiveVolatieIdGenerator { 
private static volatile long id = 0; 
public static long nextId() { 
    long nextId = (id = id + 1); // or nextId = id++; 
    return nextId; 
} 

}

4

In Java 8, utilizzare LongAdder che è anche meglio di AtomicLong cui filo la contesa è alta.

LongAdder JavaDoc:

Questa classe è solitamente preferibile AtomicLong quando più thread aggiornare una somma comune che viene utilizzato per scopi quali la raccolta dei dati, non per controllo di sincronizzazione a grana fine. Con un conflitto di aggiornamento basso, le due classi hanno caratteristiche simili. Tuttavia, in alta contesa, il rendimento atteso di questa classe è significativamente più alto, a spese di un maggiore consumo di spazio.

+0

La contesa non è alta. Solo una discussione ci scriverà. –