Anleitung zu ThreadLocalRandom in Java

Anleitung zu ThreadLocalRandom in Java

1. Überblick

Das Erzeugen von Zufallswerten ist eine sehr häufige Aufgabe. Aus diesem Grund stellt Java die Klassejava.util.Randombereit.

Diese Klasse funktioniert jedoch in einer Multithread-Umgebung nicht gut.

Vereinfacht ausgedrückt ist der Grund für die schlechte Leistung vonRandom in einer Umgebung mit mehreren Threads auf Konflikte zurückzuführen, da mehrere Threads dieselbe Instanz vonRandomgemeinsam nutzen.

Um diese Einschränkung zu beheben, müssenJava introduced the java.util.concurrent.ThreadLocalRandom class in JDK 7 – for generating random numbers in a multi-threaded environment.

Lassen Sie uns sehen, wieThreadLocalRandom funktioniert und wie es in realen Anwendungen verwendet wird.

2. ThreadLocalRandom überRandom

ThreadLocalRandom is a combination of ThreadLocal and Random classes, which is isolated to the current thread. Somit wird eine bessere Leistung in einer Multithread-Umgebung erzielt, indem einfach jeglicher gleichzeitiger Zugriff auf dieRandom-Objekte vermieden wird.

Die von einem Thread erhaltene Zufallszahl wird vom anderen Thread nicht beeinflusst, wohingegenjava.util.Random global Zufallszahlen liefert.

Im Gegensatz zuRandom, unterstütztThreadLocalRandom das explizite Festlegen des Startwerts nicht. Stattdessen überschreibt es die vonRandom geerbtesetSeed(long seed)-Methode, um beim Aufruf immer einUnsupportedOperationException auszulösen.

Schauen wir uns nun einige Möglichkeiten an, um zufälligeint, long- unddouble-Werte zu generieren.

3. Zufällige Werte mitThreadLocalRandom generieren

Gemäß der Oracle-Dokumentationwe just need to call ThreadLocalRandom.current() method, and it will return the instance of ThreadLocalRandom for the current thread. Wir können dann zufällige Werte erzeugen, indem wir verfügbare Instanzmethoden der Klasse aufrufen.

Lassen Sie uns einen zufälligenint-Wert ohne Grenzen generieren:

int unboundedRandomValue = ThreadLocalRandom.current().nextInt());

Als nächstes wollen wir sehen, wie wir einen zufällig begrenztenint-Wert generieren können, dh einen Wert zwischen einer bestimmten Unter- und Obergrenze.

Hier ist ein Beispiel für die Erzeugung eines zufälligenint-Werts zwischen 0 und 100:

int boundedRandomValue = ThreadLocalRandom.current().nextInt(0, 100);

Bitte beachten Sie, dass 0 die inklusive Untergrenze und 100 die exklusive Obergrenze ist.

Wir können zufällige Werte fürlong unddouble erzeugen, indem wir die MethodennextLong() undnextDouble() auf ähnliche Weise aufrufen, wie in den obigen Beispielen gezeigt.

Java 8 fügt außerdem dienextGaussian()-Methode hinzu, um den nächsten normalverteilten Wert mit einem Mittelwert von 0,0 und einer Standardabweichung von 1,0 von der Generatorsequenz zu generieren.

Wie bei der KlasseRandom können wir auch die Methodendoubles(), ints() undlongs() verwenden, um Ströme von Zufallswerten zu generieren.

4. Vergleichen vonThreadLocalRandom undRandom mit JMH

Lassen Sie uns sehen, wie wir mithilfe der beiden Klassen zufällige Werte in einer Umgebung mit mehreren Threads generieren und dann deren Leistung mit JMH vergleichen können.

Lassen Sie uns zunächst ein Beispiel erstellen, in dem alle Threads eine einzelne Instanz vonRandom. gemeinsam nutzen. Hier übergeben wir die Aufgabe, einen Zufallswert mithilfe der Instanz vonRandomzu generieren, anExecutorService:

ExecutorService executor = Executors.newWorkStealingPool();
List> callables = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 1000; i++) {
    callables.add(() -> {
         return random.nextInt();
    });
}
executor.invokeAll(callables);

Überprüfen Sie die Leistung des obigen Codes mithilfe des JMH-Benchmarking:

# Run complete. Total time: 00:00:36
Benchmark                                            Mode Cnt Score    Error    Units
ThreadLocalRandomBenchMarker.randomValuesUsingRandom avgt 20  771.613 ± 222.220 us/op

In ähnlicher Weise verwenden wir jetztThreadLocalRandom anstelle der InstanzRandom, die für jeden Thread im Pool eine Instanz vonThreadLocalRandom verwendet:

ExecutorService executor = Executors.newWorkStealingPool();
List> callables = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    callables.add(() -> {
        return ThreadLocalRandom.current().nextInt();
    });
}
executor.invokeAll(callables);

Hier ist das Ergebnis der Verwendung vonThreadLocalRandom:

# Run complete. Total time: 00:00:36
Benchmark                                                       Mode Cnt Score    Error   Units
ThreadLocalRandomBenchMarker.randomValuesUsingThreadLocalRandom avgt 20  624.911 ± 113.268 us/op

Wenn wir die obigen JMH-Ergebnisse sowohl fürRandom als auch fürThreadLocalRandom vergleichen, können wir klar erkennen, dass die durchschnittliche Zeit, die zum Generieren von 1000 Zufallswerten mitRandom benötigt wird, 772 Mikrosekunden beträgt, während mitRandom t3) s sind es ungefähr 625 Mikrosekunden.

Wir können also schließen, dassThreadLocalRandom is more efficient in a highly concurrent environment.

Um mehr überJMH zu erfahren, lesen Sie unsere vorherigenarticle here.

5. Fazit

Dieser Artikel illustrierte den Unterschied zwischenjava.util.Random undjava.util.concurrent.ThreadLocalRandom.

Wir haben auch den Vorteil vonThreadLocalRandom gegenüberRandom in einer Multithread-Umgebung gesehen sowie die Leistung und wie wir mithilfe der Klasse zufällige Werte generieren können.

ThreadLocalRandom ist eine einfache Ergänzung zum JDK, kann jedoch bei Anwendungen mit hoher gleichzeitiger Anwendung erhebliche Auswirkungen haben.

Und wie immer ist die Implementierung all dieser Beispiele inGitHub project zu finden.