7

I'm initializing two random number generators on two threads almost simultaneously and I want the two generators behaves completely different. I will call Random.nextInt(7) on two generators one right after another very often. Using System.currentTimeMillis() is not a good idea because it looks like my computer is so fast that there's a large chance that the number I get from the two generators are the same. So is there any way to config the Random so that though they are called one right after another, they still behave differently? I want the solution to be cross-platform compatible so any platform-specific idea such as read from /dev/random is not acceptable. Thanks for help.

YankeeWhiskey
  • 1,522
  • 4
  • 20
  • 32

5 Answers5

7

One way: Seed each Random instance with a UUID (GUID) converted (hashed? but probably not just a cast) to a long.

Other answers suggest using nanoTime, which may be suitable depending on the speed of the hardware, but I prefer the UUID route.

Community
  • 1
  • 1
Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
1

You can use System.nanoTime() which will reduce the likelihood that both threads with have the same seed.

Or create a thread-safe utility class to get for you the random number object while ensuring that it uses a different seed for each new instance.

Also, you can assign for each thread a numeric id as the thread name and append it to the seed

iTech
  • 18,192
  • 4
  • 57
  • 80
1

You can use SecureRandom to generate seeds for your two random number generators. It uses a service provider infrastructure which can use platform-specific entropy sources, so on systems where it is available, you'll likely get entropy from /dev/random without having to worry about it in your code. The UUID mentioned in another answer is generated from such a source, at least in OpenJDK 7.6.

That said, notice that since Java 7, the preferred way to use PRNGs in multiple threads is ThreadLocalRandom. I'm not sure but it seems to me that the main goal here is avoiding synchronization overhead, not seeding issues. At least in OpenJDK 7.6, the constructor uses the default Random constructor, which in turn uses the high-resolution system time (given in nanoseconds, but not neccessarily with that actual resolution), combined with the current value of a static variable. The latter ensures different seeds even for instances created during the same tick of the system clock, so your original problem should be gone even when simply constructing Random instances using the default constructor.

This uniquification was introduced in 2010, in response to bug report #6937857. The report was reported against Java 7 and was also fixed in Java 7, but according to the mercurial repository, this change should have been first included in the jdk7-b94 release.

MvG
  • 57,380
  • 22
  • 148
  • 276
  • A solution that works on Oracle java 1.6 would have more value. – AlexWien Feb 21 '13 at 19:42
  • @AlexWien: The service provider interface for `SecureRandom` dates from Java 1.2. I don't have the sources for that, so I don't know what exactly they are using behind the scenes, but my Oracle Java 1.6 ships with a `java.security` file which contains the line `securerandom.source=file:/dev/urandom`. Pretty strong indication that my first paragraph applies to Java 1.6 as well, just as I'd have expected. – MvG Feb 21 '13 at 21:55
0

You can use System.nanoTime(), instead of System.currentTimeMillis().

Just delay the creation of the second thread by 30ms, that will cause nanoTime() to be randomly differnet from nanoTime() of thread1

So there is one practical view of your question, for this view nanoTime() i consider sufficient.

The other view, is a more theoretical for military safe enryption. There you want to use special hardware that use physical randomness (random registers). But that is not your intention.

AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • 1
    There is still a likelyhood (although reduced) of the same starting seed. – hookenz Feb 11 '13 at 01:50
  • I dont think so, the nano time is not an absolute time, it is the time in nano seconds since power up of the processor, and when your system starts, it accesses, hard disk, with slighty mechanical random access speeds. So when you java programm is up, the nanoTime is random. – AlexWien Feb 11 '13 at 01:52
  • Matt is talking about 2 threads calling get nanoTime() very qclose together and receiving the same time... – Mitch Wheat Feb 11 '13 at 02:01
  • @MitchWheat Yes, then simply create two instances of Random(), start one thread and create random object with seed = nanos, then wait 100ms, and create next Random Object with seed = nano; 100ms are never exact, so it will be random, too. ready. – AlexWien Feb 11 '13 at 04:10
  • kinda defeats the purpose. You could apply the same logic to posters original scenario just with a longer wait time... – Mitch Wheat Feb 11 '13 at 04:33
  • @MitchWheat yes, you even dont need a seed, because since java 1.6 Random uses nanoTime as seed. – AlexWien Feb 11 '13 at 13:12
  • Quoting [the docs](http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime%28%29): “This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of `currentTimeMillis()`.” So although most desktop computers will provide higher resolution that milliseconds, and thus conflicts are less likely, there are no guarantees, particularly when it comes to embedded devices and the likes. – MvG Feb 21 '13 at 11:07
  • And what? what do you think that SecureRandom uses? some magic? they use nanoTime, too. – AlexWien Feb 21 '13 at 11:31
  • In OpenJDK 7, `SecureRandom` will use [`/dev/random` and `/dev/urandom`](http://tinyurl.com/awa463q) on *nix and [`CryptGenRandom`](http://tinyurl.com/bh5txtx) on windows, falling back to a [static seeder in the fallback impl](http://hg.openjdk.java.net/jdk7u/jdk7u6/jdk/file/8c2c5d63a17e/src/share/classes/sun/security/provider/SecureRandom.java). Even `Random()` does more than just use `nanoTime()`, see [my answer](http://stackoverflow.com/a/14926435/1468366) for details. – MvG Feb 21 '13 at 11:48
0

Don't do anything. It works!

This is the code of java.util.Random:

public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}

As the name says, the seedUniquifier makes the seed unique. It works both in a single thread and multithreaded:

private static long seedUniquifier() {
    // L'Ecuyer, "Tables of Linear Congruential Generators of
    // Different Sizes and Good Lattice Structure", 1999
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}
Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320