Является ли случайный класс потокобезопасным?

110

Допустимо ли разделять один экземпляр Randomкласса между несколькими потоками? И, nextInt(int)в частности, для вызова из нескольких потоков?

Щеклеин
источник
@Bala R, нет, мы говорим не о случайном объекте C #, а о Java.
Buhake Sindi
упс. извините, пропустил эту часть.
Bala R
Забота об использовании случайных чисел для получения чисел в многопоточной среде может дать вам плохие результаты. Может быть, это не имеет значения, но если вы делаете какое-то моделирование, это полезно знать.
Maxence SCHMITT
14
Для дальнейших читателей: появился новый класс с именем 1.7 java.util.concurrent.ThreadLocalRandom.
Джин Квон

Ответы:

66

Он потокобезопасен в том смысле, что он все равно будет генерировать случайные числа при использовании несколькими потоками.

Реализация Sun / Oracle JVM использует synchronized и AtomicLong в качестве начального числа для улучшения согласованности между потоками. Но, похоже, это не гарантируется для всех платформ в документации.

Я бы не стал писать вашу программу, требующую такой гарантии, тем более что вы не можете определить порядок, в котором nextInt()она будет вызвана.

Питер Лоури
источник
69
В документацию по Java 7 добавлена ​​гарантия: «Экземпляры java.util.Random безопасны для потоков». docs.oracle.com/javase/7/docs/api/java/util/Random.html
Мэтт Р.
8

Согласно документации, Math.random () гарантирует безопасность для использования несколькими потоками. Но класс Random - нет. Я предполагаю, что вам придется синхронизировать это самостоятельно.

Винсент Мимун-Прат
источник
7

Да, Random является потокобезопасным. nextInt()метод вызывает защищенный next(int)метод , который использует AtomicLong seed, nextseed(атомный длинный) для создания следующего семени. AtomicLongиспользуется для обеспечения нитевой безопасности при генерации семян.

Бухаке Синди
источник
6

Как уже было сказано, это сохранение потока, но может быть разумно использовать в java.util.concurrent.ThreadLocalRandomсоответствии с этой статьей (ссылка мертва). ThreadLocalRandom также является подклассом Random, поэтому он обратно совместим.

В статье связанные между собой сравниваемых результаты различных классов случайных профилирование: java.util.Random, java.util.concurrent.ThreadLocalRandom и java.lang.ThreadLocal<java.util.Random>. Результаты показали, что использование ThreadLocalRandom является наиболее эффективным, за ним следует ThreadLocal и хуже всего работает сам Random.

Сейфахни
источник
4

Нет причин, по которым несколько потоков не могут использовать одно и то же Random. Однако, поскольку класс не является явно потокобезопасным и поддерживает последовательность псевдослучайных чисел через начальное число. Несколько потоков могут иметь одно и то же случайное число. Было бы лучше создать несколько Random для каждого потока и по-разному их засеять.

РЕДАКТИРОВАТЬ : Я только что заметил, что реализация Sun использует AtomicLong, поэтому я предполагаю, что это потокобезопасный (как также отметил Питер Лоури (+1)).

EDIT2 : OpenJDK также использует AtomicLong для семени. Как уже говорили другие, полагаться на это по-прежнему нецелесообразно.

альпийский
источник
3

Вот как я решил проблему, не предполагая, что Random использует атомарные переменные. Он все еще может случайным образом столкнуться, если currentTime * thread idв какое-то время будет одинаково, но это достаточно редко для моих нужд. Чтобы действительно избежать возможности коллизий, вы можете заставить каждый запрос ожидать уникальную временную метку часов.

/**
 * 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());
    }
};
Райан
источник
Вверх! Q: (24*60*60*1000)часть значима?
Джин Квон,
1
Да, это было грязное решение. Дело в (24*60*60*1000)том, что поток с идентификатором 12в xxxxxxxxxx045миллисекундах не засевается так же, как поток 22в xxxxxxxxxx035миллисекундах. Однако у меня нет веских оснований предполагать, что идентификаторы потоков являются инкрементными, и нет веских причин думать, что завтра я создаю потоки в более случайное время, чем сегодня. Я упростил алгоритм и обновил описание, чтобы выявить недостаток.
Райан
0

RandomКласс не настроен для одного экземпляра , которые будут использоваться в нескольких потоках. Конечно, если вы это сделаете, вероятно, вы увеличите вероятность непредсказуемости и приближения к случайным числам. Но поскольку это псевдослучайный генератор, я не понимаю, зачем вам нужно делиться экземпляром. Есть ли более конкретные требования?

Ява Пьяница
источник