Langer primitiver oder atomiclong für einen Zähler?
-
20-09-2019 - |
Frage
Ich brauche einen Schalter vom Typ long
mit den folgenden Anforderungen/Fakten:
- Das Inkrementieren des Zählers sollte so wenig Zeit wie möglich dauern.
- Der Zähler wird nur von einem Thread geschrieben.
- Das Lesen aus der Theke erfolgt in einem anderen Thread.
- Der Zähler wird regelmäßig inkrementiert (bis zu ein paar tausendmal pro Sekunde), aber nur alle fünf Sekunden gelesen.
- Genauige Genauigkeit ist nicht wesentlich, nur eine grobe Vorstellung von der Größe des Zählers ist gut genug.
- Der Zähler wird niemals gelöscht, abgeschlossen.
Wie würden Sie basierend auf diesen Anforderungen Ihren Zähler implementieren? Als einfach long
, Als ein volatile long
oder mit einem AtomicLong
? Wieso den?
Im Moment habe ich eine volatile long
Aber fragte sich, ob ein anderer Ansatz besser wäre. Ich erhöhe auch meine lange, indem ich es mache ++counter
im Gegensatz zu counter++
. Ist das wirklich effizienter (wie ich anderswo glauben geführt wurde), weil keine Aufgabe erfolgt?
Lösung
Angesichts dieser Anforderungen, i denken das a volatile
lang sollte ausreichen. Der Zähler wäre mit einem Nichts nicht falschvolatile
Lang, aber der Leser könnte in diesem Fall abgestandene Informationen lesen.
Ein Problem ist, dass es liest und auf a schreibt long
sind nicht erforderlich Atomic sein, bis zum JVM -Spezifikation Wenn es nicht deklariert wird volatile
. Das würde bedeuten, dass der Lese -Thread einen ziemlich fiktiven Wert erzielen könnte, wenn er den Wert liest, während der Schreib -Thread einen Teil des Wertes aktualisiert hat, aber nicht den anderen.
Der Unterschied zwischen ++counter
und counter++
ist wahrscheinlich irrelevant, da der JVM erkennen wird, dass der Wert des Ausdrucks nicht mehr verwendet wird und die beiden in diesem Fall gleichwertig sind.
Andere Tipps
Verwenden Sie in Java 8 Longadder, was sogar noch besser ist als Atomiclong, wo die Gewinne hoch ist.
Longadder Javadoc:
Diese Klasse ist Atomiclong normalerweise vorzuziehen, wenn mehrere Threads eine gemeinsame Summe aktualisieren, die für Zwecke wie das Sammeln von Statistiken und nicht für die feinkörnige Synchronisationskontrolle verwendet wird. Unter geringer Aktualisierung weisen die beiden Klassen ähnliche Eigenschaften auf. Unter hoher Aussage ist der erwartete Durchsatz dieser Klasse auf Kosten eines höheren Raumverbrauchs signifikant höher.
Was ist die Verfügbarkeit für Ihr Programm? Könnten Sie mit einem unflüchtigen Int- und Racy-Lesen auskommen?
10^4 Inkremente / Sekunde ist 1 100 usec. Effizienz ist kein Problem, aber Atomizität könnte sein. Möglicherweise haben Sie 2 Kopien davon, und wenn es gelesen wird, wenn sie nicht gleich sind, lesen Sie erneut.
Dies Artikel Gespräche über die möglichen Möglichkeiten zur Implementierung eines Zählers. Ich denke, diese Implementierung sollte für Sie funktionieren
class LessNaiveVolatieIdGenerator {
private static volatile long id = 0;
public static long nextId() {
long nextId = (id = id + 1); // or nextId = id++;
return nextId;
}
}