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

110

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

Shcheklein
la source
@Bala R, non, nous ne parlons pas de l'objet Random de C # mais de celui de Java.
Buhake Sindi
Oups. désolé a manqué cette partie.
Bala R
Prendre soin d'utiliser Random pour obtenir des nombres dans un environnement multithread peut vous donner de mauvais résultats. Peut-être que cela n'a pas d'importance, mais si vous faites des simulations, il est bon de savoir.
Maxence SCHMITT
14
Pour les autres lecteurs: il existe une nouvelle classe nommée 1.7 java.util.concurrent.ThreadLocalRandom.
Jin Kwon

Réponses:

66

Il est thread-safe dans le sens où il génère toujours des nombres aléatoires lorsqu'il est utilisé par plusieurs threads.

L'implémentation de la JVM Sun / Oracle utilise synchronized et AtomicLong comme amorce pour améliorer la cohérence entre les threads. Mais cela ne semble pas être garanti sur toutes les plates-formes 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é.

Peter Lawrey
la source
69
Une garantie a été ajoutée dans la documentation Java 7: "Les instances de java.util.Random sont threadsafe." docs.oracle.com/javase/7/docs/api/java/util/Random.html
Matt R
8

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

Vincent Mimoun-Prat
la source
7

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

Buhake Sindi
la source
6

Comme dit, il s'agit d'une sauvegarde de thread, mais il peut être judicieux d'utiliser java.util.concurrent.ThreadLocalRandomselon cet article (lien mort). ThreadLocalRandom est également une sous-classe de Random, il est donc rétrocompatible.

L'article Linked a comparé les résultats de profilage des différentes classes au hasard: 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 la pire performance Random lui-même.

seyfahni
la source
4

Il n'y a aucune raison pour laquelle plusieurs threads ne peuvent pas tous utiliser le même Random. 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 aléas pour chaque thread et de les semer différemment.

EDIT : Je viens de remarquer que l'implémentation 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 se fier à cela.

alpian
la source
3

Voici comment j'ai traité le problème sans supposer que Random utilise des variables atomiques. Il peut encore entrer en collision de manière aléatoire si elle currentTime * thread idest égale dans le futur, mais c'est assez rare pour mes besoins. Pour vraiment éviter la possibilité de collisions, vous pouvez demander à chaque demande d'attendre 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());
    }
};
Ryan
la source
Up! Q: la (24*60*60*1000)partie est -elle significative?
Jin Kwon
1
Ouais, c'était une sale solution. Le (24*60*60*1000)était de sorte qu'un fil d'ID 12à xxxxxxxxxx045Millis ne s'ensemencée même comme un fil 22à xxxxxxxxxx035Millis. Cependant, je n'ai aucune bonne raison de supposer que les ID de threads sont incrémentiels, et il n'y a aucune bonne raison de penser que je crée des threads à des moments plus aléatoires demain qu'aujourd'hui. J'ai simplifié l'alg maintenant et mis à jour la description pour identifier le défaut.
Ryan
0

La Randomclasse n'est pas configurée pour qu'une instance soit utilisée dans plusieurs threads. Bien sûr, si vous faites cela, vous augmenterez probablement la possibilité de devenir imprévisible et plus proche des nombres 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?

Buveur Java
la source