20

I'm looking to use (seeded) Random objects across multiple threads, and the javadocs pointed me to ThreadLocalRandom which looks great except I can't set the seed, so I can't ensure consistency among different threads or runs. Is there any practical reason to use ThreadLocalRandom or would it be acceptable to do something like the following:

// 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);
        }
    };
}
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • 1
    Do you want to share one Random instance among different threads ? Or make sure each thread has its own instance of Random ? – Jan Goyvaerts Apr 02 '13 at 13:59
  • According to the documentation for `Random` sharing one `Random` object between threads is a poor idea. I just want to control the seed of the random objects being used so the behavior is consistent and repeatable. – dimo414 Apr 02 '13 at 14:49
  • You could inject a seed with reflection (set initialized to false, call setSeed and set initialized back to true). – assylias Apr 02 '13 at 18:32
  • Usually, there's no such thing like consistency when working with multiple threads. Your solution is fine, just a bit slower than `ThreadLocalRandom`, especially in JDK8. – maaartinus Nov 03 '13 at 11:38

4 Answers4

10

You can simply use Random, just make sure each Random object is only accessed within a single thread.

Random, being an ancient class like Vector, is unnecessarily heavily synchronized. They probably wanted to show off Java's threading support since it was a big deal at that time. Also Java was mostly intended to run on consumer PCs which mostly had a single processor so synchronization didn't impact scaling like it does today on multiprocessors.

Now an obvious answer is to provide a thread-unsafe version of Random, just like providing the thread-unsfae ArrayList as the alternative to Vector. That didn't happen, instead, we got ThreadLocalRandom. That is kind of odd, not sure what's the motivation behind that. In java8, ThreadLocalRandom is further optimized to operate directly on some int fields in the Thread object.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61
1

The code for ThreadLocalRandom appears to be implemented as a ThreadLocal anyway (not exactly like you've put it, but probably close enough). I think what you have will work great.

Rob I
  • 5,627
  • 2
  • 21
  • 28
  • 2
    1. In Java 8, there are designated fields in Thread for ThreadLocalRandom, so ThreadLocal is not used any more. 2. While using ThreadLocal does eliminate contention, unnecessary checking is still on in Random. Therefore its performance would not be comparable to ThreadLocalRandom (even in Java 7). – kavinyao Sep 18 '14 at 03:33
  • 1
    @kavinyao uncontended synchronization in java is quite fast these days. – Matt Ball Apr 05 '16 at 19:29
1

Depends on nature of seed required and ThreadLocalRandom does not allow setting the Random seed. There are cases where you may want to seed Random by, say, "user" from current request so "randomness is consistent for a user" and as containers reuse threads (few) for new requests and new Random() is expensive, you may want to create your own ThreadLocal. As Java ThreadLocalRandom does not support seed, you can write it easily in JDK 1.8:

public class RandomUtil {

 private static final ThreadLocal<Random> RANDOM_THREAD_LOCAL = ThreadLocal.withInitial(Random::new);

 public static Random threadLocalRandom(long seed){
    Random random = RANDOM_THREAD_LOCAL.get();
    random.setSeed(seed);
    return random;
}

As each Random is local to a thread and thread safe, it can be re seeded "if needed" for next request/user without creating a new Random(). This should way performant than creating new Random(user seed) for each request but slower than Java ThreadLocalRandom with its internal optimizations.

kisna
  • 2,869
  • 1
  • 25
  • 30
0

First, your code uses the same seed for every thread. That can be a security problem. Moreover, every access to the membery of your instance of Random is synchronized, so it will be slower than the corresponding methods of ThreadLocalRandom (which are not synchronized). If the compiler could be sure, that your instance of Random does not escape the current Thread, it could optimize those "synchronized" away. but As you can store shared references in ThreadLocal there is no way for the compiler to check this.

Steffen Heil
  • 4,286
  • 3
  • 32
  • 35