web-dev-qa-db-fra.com

Le thread de classe aléatoire est-il sûr?

Est-il valide de partager une instance de la classe Random entre plusieurs threads? Et pour appeler nextInt(int) à partir de plusieurs threads en particulier?

104
Shcheklein

Il est sûr pour les threads dans le sens où il générera toujours des nombres aléatoires lorsqu'il est utilisé par plusieurs threads.

L'implémentation JVM Sun/Oracle utilise synchronized et AtomicLong comme graine pour améliorer la cohérence entre les threads. Mais il ne semble pas être garanti sur toutes les plateformes dans la documentation.

Je n'écrirais pas votre programme pour exiger une telle garantie, d'autant plus que vous ne pouvez pas déterminer l'ordre dans lequel nextInt() sera appelé.

63
Peter Lawrey

Il est sûr pour les threads, bien que ce ne soit pas toujours le cas.

Voir http://bugs.Sun.com/bugdatabase/view_bug.do?bug_id=636207 pour plus de détails.

23
iggymoran

Selon la documentation, Math.random () garantit qu'il est sûr pour une utilisation par plusieurs threads. Mais la classe Random ne fonctionne pas. Je suppose que vous devrez alors synchroniser cela vous-même.

8

Oui, Random est thread-safe. la méthode nextInt() appelle la méthode protégée next(int) qui utilise AtomicLong seed, nextseed (atomic long) pour générer une graine suivante. AtomicLong est utilisé pour la sécurité des threads lors de la génération de graines.

7
Buhake Sindi

Comme nous l'avons dit, il s'agit d'un thread save, mais il peut être judicieux d'utiliser Java.util.concurrent.ThreadLocalRandom selon cet article (lien mort). ThreadLocalRandom est également une sous-classe de Random, il est donc rétrocompatible.

L'article a lié les résultats de profilage comparés des différentes classes aléatoires: Java.util.Random, Java.util.concurrent.ThreadLocalRandom et Java.lang.ThreadLocal<Java.util.Random>. Les résultats ont montré que l'utilisation de ThreadLocalRandom est la plus performante, suivie de ThreadLocal et de Random elle-même la moins performante.

5
seyfahni

Il n'y a aucune raison pour que plusieurs threads ne puissent pas tous utiliser le même aléatoire. Cependant, puisque la classe n'est pas explicitement thread-safe et maintient une séquence de nombres pseudo-aléatoires via la graine. Plusieurs threads peuvent se retrouver avec le même nombre aléatoire. Il serait préférable de créer plusieurs Randoms pour chaque thread et de les amorcer différemment.

EDIT : Je viens de remarquer que l'implémentation de Sun utilise AtomicLong donc je suppose que c'est Thread-safe (comme l'a également noté Peter Lawrey (+1)) .

EDIT2: OpenJDK utilise également AtomicLong pour la graine. Comme d'autres l'ont dit, il n'est toujours pas bon de s'en remettre à cela.

4
alpian

Voici comment j'ai traité le problème sans supposer que Random utilise des variables atomiques. Il peut toujours entrer en collision au hasard si currentTime * thread id est égal dans le futur, mais c'est assez rare pour mes besoins. Pour vraiment éviter la possibilité de collisions, vous pourriez avoir chaque demande d'attente pour un horodatage d'horloge unique.

/**
 * Thread-specific random number generators. Each is seeded with the thread
 * ID, so the sequence of pseudo-random numbers are unique between threads.
 */
private static ThreadLocal<Random> random = new ThreadLocal<Random>() {
    @Override
    protected Random initialValue() {
        return new Random(
            System.currentTimeMillis() *
            Thread.currentThread().getId());
    }
};
3
Ryan

La classe Random n'est pas configurée pour une instance à utiliser dans plusieurs threads. Bien sûr, si vous avez fait cela, vous augmenterez probablement la possibilité d'obtenir des chiffres imprévisibles et plus proches de aléatoires. Mais comme il s'agit d'un générateur pseudo-aléatoire, je ne vois pas pourquoi vous auriez besoin de partager une instance. Existe-t-il une exigence plus spécifique?

0
Java Drinker