По какой-то причине я полагал, что java.util.Random
является поточно-опасным, a-la HashMap
или BitSet
, а Math.random()
реализуется либо как обертывающий доступ к Random
с блоком synchronized
или ThreadLocalRandom.current().nextDouble()
.
На самом деле оказывается, что java.util.Random
является потокобезопасным (через атомику). Следовательно, вынос: даже если мне нужен какой-то случайный ввод в одном потоке, имеет смысл использовать ThreadLocalRandom
, потому что внутри не существует атомарного чтения и записи, скомпилированного как заблокированные инструкции и испускающие барьеры памяти.
Более того, поскольку Java 8, ThreadLocalRandom
по существу является одноэлементным, его состояние сохраняется в некоторых полях класса java.lang.Thread
. Поэтому метод ThreadLocalRandom.current()
не является доступом к ThreadLocalMap
, а только прочитанным статическим полем, т.е. е. очень дешево.
У меня есть два вопроса:
-
С точки зрения компьютерных наук, выход нескольких линейных конгруэнтных случайных генераторов (инициализированных способом
ThreadLocalRandom
) является таким же "случайным", как выход одиночного линейного конгруэнтного случайного генератора (java.util.Random
экземпляр)? -
Если ответ на первый вопрос есть Да, есть ли причина написать конструкцию
new Random()
(без семени) вместоThreadLocalRandom.current()
, когда-либо?
Update. Я предположил, что вызовы типа ThreadLocalRandom.current().ints().parallel().collect(...)
могут быть некорректными, потому что случайное состояние генератора Thread не может быть инициализировано в ForkJoinPool
рабочих потоках, но появляется, что ThreadLocalRandom
переопределяет методы ints()
, longs()
и doubles()
, что делает выше правильной конструкции.