Guide de ThreadLocalRandom en Java

Guide de ThreadLocalRandom en Java

1. Vue d'ensemble

Générer des valeurs aléatoires est une tâche très courante. C'est pourquoi Java fournit la classejava.util.Random.

Cependant, cette classe ne fonctionne pas bien dans un environnement multi-thread.

De manière simplifiée, la raison de la mauvaise performance deRandom dans un environnement multi-thread est due à la contention - étant donné que plusieurs threads partagent la même instanceRandom.

Pour remédier à cette limitation,Java introduced the java.util.concurrent.ThreadLocalRandom class in JDK 7 – for generating random numbers in a multi-threaded environment.

Voyons commentThreadLocalRandom fonctionne et comment l’utiliser dans des applications réelles.

2. ThreadLocalRandom surRandom

ThreadLocalRandom is a combination of ThreadLocal and Random classes, which is isolated to the current thread. Ainsi, il obtient de meilleures performances dans un environnement multithread en évitant simplement tout accès simultané aux objetsRandom.

Le nombre aléatoire obtenu par un thread n'est pas affecté par l'autre thread, alors quejava.util.Random fournit des nombres aléatoires globalement.

De plus, contrairement àRandom,,ThreadLocalRandom ne prend pas en charge la définition explicite de la valeur de départ. Au lieu de cela, il remplace la méthodesetSeed(long seed) héritée deRandom pour toujours lancer unUnsupportedOperationException s'il est appelé.

Examinons maintenant quelques-unes des façons de générer des valeurs aléatoiresint, long etdouble.

3. Génération de valeurs aléatoires à l'aide deThreadLocalRandom

Selon la documentation Oracle,we just need to call ThreadLocalRandom.current() method, and it will return the instance of ThreadLocalRandom for the current thread. Nous pouvons ensuite générer des valeurs aléatoires en appelant des méthodes d'instance disponibles de la classe.

Générons une valeur aléatoire deint sans aucune limite:

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

Voyons ensuite comment nous pouvons générer une valeurint bornée aléatoire, c'est-à-dire une valeur entre une limite inférieure et supérieure donnée.

Voici un exemple de génération d'une valeur aléatoireint comprise entre 0 et 100:

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

Veuillez noter que 0 correspond à la limite inférieure incluse et à 100 à la limite supérieure exclusive.

Nous pouvons générer des valeurs aléatoires pourlong etdouble en appelant les méthodesnextLong() etnextDouble() de la même manière, comme indiqué dans les exemples ci-dessus.

Java 8 ajoute également la méthodenextGaussian() pour générer la prochaine valeur normalement distribuée avec une moyenne de 0,0 et un écart type de 1,0 par rapport à la séquence du générateur.

Comme pour la classeRandom, nous pouvons également utiliser les méthodesdoubles(), ints() etlongs() pour générer des flux de valeurs aléatoires.

4. Comparaison deThreadLocalRandom etRandom à l'aide de JMH

Voyons comment nous pouvons générer des valeurs aléatoires dans un environnement multi-thread, en utilisant les deux classes, puis comparer leurs performances à l'aide de JMH.

Tout d'abord, créons un exemple où tous les threads partagent une seule instance deRandom. Ici, nous soumettons la tâche de génération d'une valeur aléatoire à l'aide de l'instanceRandom à unExecutorService:

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);

Vérifions les performances du code ci-dessus à l'aide de l'analyse comparative JMH:

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

De même, utilisons maintenantThreadLocalRandom au lieu de l'instanceRandom, qui utilise une instance deThreadLocalRandom pour chaque thread du pool:

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

Voici le résultat de l'utilisation deThreadLocalRandom:

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

Enfin, en comparant les résultats JMH ci-dessus pourRandom etThreadLocalRandom, nous pouvons clairement voir que le temps moyen nécessaire pour générer 1000 valeurs aléatoires en utilisantRandom est de 772 microsecondes, alors qu'en utilisantThreadLocalRandom c'est environ 625 microsecondes.

Ainsi, nous pouvons conclure queThreadLocalRandom is more efficient in a highly concurrent environment.

Pour en savoir plus sur lesJMH, consultez nos précédentsarticle here.

5. Conclusion

Cet article a illustré la différence entrejava.util.Random etjava.util.concurrent.ThreadLocalRandom.

Nous avons également vu l'avantage deThreadLocalRandom surRandom dans un environnement multithread, ainsi que les performances et la façon dont nous pouvons générer des valeurs aléatoires à l'aide de la classe.

ThreadLocalRandom est un simple ajout au JDK, mais il peut créer un impact notable dans les applications hautement simultanées.

Et, comme toujours, l'implémentation de tous ces exemples peut être trouvée dans lesGitHub project.