Longue primitive ou AtomicLong pour un compteur?
-
20-09-2019 - |
Question
J'ai besoin d'un compteur de type long
avec les exigences / faits suivants:
- incrémenter le compteur devrait prendre aussi peu de temps que possible.
- Le compteur ne sera écrit par un fil.
- La lecture du compteur se fera dans un autre thread.
- Le compteur est incrémenté régulièrement (jusqu'à quelques milliers de fois par seconde), mais ne sera lu une fois toutes les cinq secondes.
- Précision précise n'est pas essentiel, seule une idée approximative de la taille du compteur est assez bon.
- Le compteur est jamais effacé, décrémenté.
Sur la base de ces exigences, comment voulez-vous choisir d'implémenter votre compteur? En tant que long
simple, comme volatile long
ou en utilisant un AtomicLong
? Pourquoi?
En ce moment j'ai un volatile long
mais se demandait si une autre approche serait mieux. Je suis également incrémentant mon long en faisant ++counter
par opposition à counter++
. Est-ce vraiment plus efficace (comme je l'ai été amené à croire ailleurs) parce qu'il n'y a pas d'affectation fait?
La solution
Compte tenu de ces ensembles d'exigences, I pense que volatile
longue devrait être suffisant. Le compteur ne serait pas incorrect avec un non-volatile
long, mais le lecteur peut-être lire des informations périmées dans ce cas.
Un problème est que les lectures et écritures à un long
sont pas requis être atomique, par le spécification JVM si elle n'est pas déclarée volatile
. Cela voudrait dire que le fil de la lecture pourrait avoir une valeur à peu près si elle lit fictive la valeur alors que le fil d'écriture a mis à jour une partie de la valeur, mais pas l'autre.
La différence entre ++counter
et counter++
est probablement non pertinentes, comme la machine virtuelle Java se rendra compte que la valeur de l'expression n'est pas utilisé plus et les deux sont équivalents dans ce cas.
Autres conseils
En Java 8, utilisez LongAdder ce qui est encore mieux que AtomicLong où les conflits de fil est élevé.
LongAdder JavaDoc:
Cette classe est habituellement préférable de AtomicLong lorsque plusieurs threads sont mis à jour une somme courante qui est utilisée à des fins telles que la collecte de statistiques, et non pas pour le contrôle de la synchronisation fine. Sous contention faible mise à jour, les deux classes ont des caractéristiques similaires. Mais sous haute contention, le débit attendu de cette classe est nettement plus élevé, au détriment de la consommation d'espace supérieur.
ce qui est l'exigence de disponibilité de votre programme? Pourriez-vous faire avec un non volatile int et racé-lit?
10 ^ 4 incréments / seconde est une tranche de 100 microseconde. L'efficacité est pas un problème, mais peut-être atomicité. Vous pourriez avoir 2 copies, et quand il est lu, si elles ne sont pas égaux, lire à nouveau.
class LessNaiveVolatieIdGenerator {
private static volatile long id = 0;
public static long nextId() {
long nextId = (id = id + 1); // or nextId = id++;
return nextId;
}
}