12

I want to understand what the difference is between using a Random number generator with System.currentTimeMillis() as the seed and just using the default constructor. That is, what is the difference between this:

Random rand = new Random(System.currentTimeMillis());

and this:

Random rand = new Random();

I know that the numbers are pseudo-random, but I am yet to fully understand the details, and how they come about, between the level of 'randomness' one gets when current time is used as seed, and when the default constructor is used.

ayePete
  • 411
  • 1
  • 7
  • 23
  • Can you tell us which articles, guides, or tutorials you've looked up before asking this question? It may help us dial in on what exactly you're not understanding. – CubeJockey Aug 28 '15 at 17:59
  • 1
    http://stackoverflow.com/questions/20060725/default-seed-prng-in-java – arunmoezhi Aug 28 '15 at 17:59

3 Answers3

8

Supplying your own seed is useful for simulations where you purposely want to generate the same sequence of pseudorandom values multiple times. In general, though, it's just as well to use the default constructor.

When the default constructor is used, the docs say:

This constructor sets the seed of the random number generator to a value very likely to be distinct from any other invocation of this constructor.

In other words, it generates its own seed internally. The details depend on the particular Java implementation being used. One implementation I've seen has this:

private static volatile long seedBase = 0;

public Random() {
    setSeed(System.nanoTime() + seedBase);
    ++seedBase;
}

The actual quality of the randomness doesn't change. If the quality of the random sequence is of concern to you, you can also use java.security.SecureRandom, which has better cryptographic behavior. (See, e.g., this thread.)

Community
  • 1
  • 1
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • I'm curious, where did you see that implementation? In addition to a misuse of `++` on a volatile field, that's significantly simpler than the one that JDK 7 or [JDK 8](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/Random.java#l105) use. – yshavit Aug 28 '15 at 18:07
  • 1
    @yshavit - That's the one used in Android's API 22 library. – Ted Hopp Aug 28 '15 at 18:08
  • @yshavit [JDK 6](http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/814bf0775b52/src/share/classes/java/util/Random.java#l77) is equally simple. – Andreas Aug 28 '15 at 18:12
  • @yshavit - I don't think that the race condition on the volatile field is a serious concern; even if two threads overwrite each other, the odds of `System.nanoTime() + seedBase` being the same on subsequent calls is quite small and in any event the contract only guarantees "very likely to be distinct". I agree with you about this being a poor implementation; it would have been much better to use `AtomicLong`. – Ted Hopp Aug 28 '15 at 18:16
  • @TedHopp I totally agree, it's not a serious concern at all -- though I disagree about the odds of similar seeds: if two threads are racing close enough that they overwrite each other, it's quite plausible that they'll have the same `nanoTime()`, especially given that most platforms don't actually give you nanosecond precision (my Mac seems to "only" provide microsecond precision). But, like you, I would have thought an AtomicLong would be the better way to go -- to set up good habits, if nothing else! – yshavit Aug 28 '15 at 19:02
7

If you want your random sequences to be the same between runs you can specify a seed. Usually you don't want that to happen so you use a different seed for every run and System.currentTimeMillis() is reasonable seed commonly used.

If you are writing a multithreaded program where multiple threads will initialize the Random object at the same time, then you may want to avoid using System.currentTimeMillis() and instead let Java use its own initialization.

Xiddoc
  • 3,369
  • 3
  • 11
  • 37
Josep Valls
  • 5,483
  • 2
  • 33
  • 67
  • 4
    In general, you *always* want to let Java use its own initialization. It's better than System.currentTimeMillis(), so why would you ever want to downgrade to that? – Andreas Aug 28 '15 at 18:06
  • @Andreas Thanks! You cleared my confusion with this comment! I've always thought that using using `System.currentTimeMillis()` is kinda superflous. Am I right? . In fact, in one of my programs where I was using the Random class, I was surprised that when I used `System.currentTimeMillis()` the performance was worse. I couldn't figure out what the reason for that was. I was basically just confused, but I was like, well, let's just used the one that works! BTW, I don't know if I should mark this answer as correct, cos it's this comment that really answers my question! – ayePete Aug 28 '15 at 18:46
  • @Andreas I would really appreciate it if you could give a more detailed answer based on what you said in the above comment, so I could accept it. Thanks a lot! – ayePete Aug 28 '15 at 18:49
  • 1
    @ayePete System.currentTimeMillis() is defnitely superfluous. I am not sure what you mean by "degraded performance". Are you initializing multiple RNG? You may want to use a Singleton pattern or just keep a single RNG for every thread. – Josep Valls Aug 28 '15 at 18:53
  • @Josep Valls I basically do research on using heuristics to solve Combinatorial Optimization Problems, so in the case I was referring to, the solution gotten was closer to the optimal, and more frequently, with the default constructor than with `System.currentTimeMillis`. From what I can recall, I was initializing multiple RNG at various points in the code, so I don't know if that had any effect on the values generated. Are indirectly recommending using a single RNG throughout the program? I used a single thread. – ayePete Aug 28 '15 at 19:02
  • 1
    @ayePete Even the simpler implementation in JDK 6 is using `System.nanoTime()` with a changing seed, reducing multi-threaded clashes. `System.currentTimeMillis()` is a downgrade from that built-in implementation. Moral of the story: *Don't* specify a seed, *unless* you want to re-use the seed for multiple runs. – Andreas Aug 28 '15 at 19:17
2

If you look into the implementation of the default constructor of Random, you can see that it uses System.nanoTime() internally. In addition, it uses a seed 'uniquifier' to make subsequent seeds even more distinct. This, however, requires access to a static final AtomicLong. Thus if you have a highly concurrent application where many threads are constructing Random instances, it might be better to not use the default constructor to avoid contention on the seed generation. If you want to guarantee that two threads can never get the same seed you should take the default constructor. This said, in practice, in 99% of the cases, it will be irrelevant which variant you take.

As Ted Hopp correctly states, the behavior of the default constructor depends on the concrete JDK implementation and also varies between Java versions.

Also see Contention in concurrent use of java.util.Random for another contention issue with the Random class.

Community
  • 1
  • 1
Jan Schaefer
  • 1,882
  • 1
  • 18
  • 23
  • The details of how a seed is generated internally varies with the Java implementation. With Java 7 and 8, the "uniquifier" is an `AtomicLong`, which avoids the multi-threading problems you describe. – Ted Hopp Aug 28 '15 at 18:21
  • It depends on the use case. If you want to avoid in any case to have the same seed for different threads you are right. However, if you want to avoid contention and can live with identical seeds, the AtomicLong is a problem. – Jan Schaefer Aug 28 '15 at 18:23
  • Whether `AtomicLong` is a problem or not, my point was simply that if "you look into the implementation of the default constructor of `Random`" as you say, you'll find different things depending on what version of Java you're using. In some implementations, the "uniquifier" is simply a `volatile` field. There is no thread contention but there is a race condition (and no _guarantee_ of uniqueness). – Ted Hopp Aug 28 '15 at 18:33
  • Sure you are right that there are different implementations. Still it is relevant if you are using Java 7 or 8. Also see http://stackoverflow.com/questions/22313552/contention-in-concurrent-use-of-java-util-random – Jan Schaefer Aug 28 '15 at 18:48