По какой-то причине я полагал, что 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(), что делает выше правильной конструкции.