Должен ли я использовать ThreadLocalRandom для ThreadLocal <Random>?

Я хочу использовать (посеянные) объекты Random для нескольких потоков, а javadocs указали мне на ThreadLocalRandom, который выглядит отлично, за исключением того, что я не могу установить семя, поэтому я не могу обеспечить согласованность между различными потоками или прогонами. Есть ли практическая причина использовать ThreadLocalRandom или было бы приемлемо сделать что-то вроде следующего:

// Pass returned ThreadLocal object to all threads which need it
public static ThreadLocal<Random> threadRandom(final long seed) {
    return new ThreadLocal<Random>(){
        @Override
        protected Random initialValue() {
            return new Random(seed);
        }
    };
}

Ответ 1

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

Random, будучи древним классом вроде Vector, излишне синхронизирован. Вероятно, они хотели продемонстрировать поддержку потоков Java, поскольку в то время это было большой проблемой. Кроме того, Java в основном предназначалась для работы на потребительских ПК, в которых в основном использовался один процессор, поэтому синхронизация не влияла на масштабирование, как сегодня, на многопроцессорных системах.

Теперь очевидным ответом является предоставление небезопасной версии Random с потоком, так же, как предоставление thread-unsfae ArrayList в качестве альтернативы Vector. Этого не произошло, вместо этого мы получили ThreadLocalRandom. Это странно, не уверен, в чем причина этого. В java8 ThreadLocalRandom дополнительно оптимизируется для работы непосредственно с некоторыми int-полями в объекте Thread.

Ответ 2

Код для ThreadLocalRandom, по-видимому, реализован как ThreadLocal в любом случае (не так, как вы его выразили, но, вероятно, достаточно близко). Я думаю, что у вас будет отлично работать.

Ответ 3

Во-первых, ваш код использует одно и то же семя для каждого потока. Это может быть проблемой безопасности. Более того, каждый доступ к члену вашего экземпляра Random синхронизирован, поэтому он будет медленнее, чем соответствующие методы ThreadLocalRandom (которые не синхронизированы). Если бы компилятор мог быть уверен, что ваш экземпляр Random не ускользнет от текущего потока, он может оптимизировать "синхронизированный". но поскольку вы можете хранить общие ссылки в ThreadLocal, компилятор не может проверить это.