A Random Pitfall

  • February 16, 2017
Table Of Contents

From time to time we need a randomly generated Number in Java. In this case we are normally using java.util.Random which provides a stream of pseudo generated Numbers. But there are some use cases in which the direct usage may cause some unexpected problems.

This is the ordinary way to generate a Number:

// Random
Random random = new Random();
random.nextInt();//nextDouble(), nextBoolean(), nextFloat(), ...

Alternatively, we can use the Math Class:

// Math
Math.random();

Whereby the Math class just holds an instance of Random to generating Numbers.

// Math
public static double random() {
    Random rnd = randomNumberGenerator;
    if (rnd == null) rnd = initRNG(); // return a new Random Instance
    return rnd.nextDouble();
}

According to the Javadoc, the usage of java.util.Random is thread safe. But the concurrent use of the same Random instance across different threads may cause contention and consequently poor performance. The reason for this is the usage of so called Seeds for the generation of random numbers. A Seed is a simple number which provides the basis for the generation of new random numbers. This happens within the method next() which is used within Random:

// Random
protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}

First, the old seed and a new one are stored over two auxiliary variables. The principle by which the new seed is created is not important at this point. To save the new seed, the compareAndSet() method is called. This replaces the old seed with the next new seed, but only under the condition that the old seed corresponds to the seed currently set. If the value in the meantime was manipulated by a concurrent thread, the method return false, which means that the old value did not match the excepted value. This is done within a loop till the variables matches the excepted values. And this is the point which could cause poor performance and contention.

Thus, if more threads are actively generating new random numbers with the same instance of Random, the higher the probability that the above mentioned case occurs. For programs that generate many (very many) random numbers, this procedure is not recommended. In this case you should use ThreadLocalRandom instead, which was added to Java in version 1.7.

ThreadLocalRandom extends Random and adds the option to restrict its use to the respective thread instance. For this purpose, an instance of ThreadLocalRandom is held in an internal map for the respective thread and returned by calling current().

ThreadLocalRandom.current().nextInt()

Conclusion

The pitfall described above does not mean that it’s forbidden to share a Random Instance between several threads. There is no problem with turning one or two extra rounds in a loop, but if you generate a huge amount of random numbers in different threads, just bear the above mentioned solution in mind. This could save you some debug time :)

Written By:

Rudi Klassen

Written By:

Rudi Klassen

Recent Posts

Guide to JUnit 5 Functional Interfaces

In this article, we will get familiar with JUnit 5 functional interfaces. JUnit 5 significantly advanced from its predecessors. Features like functional interfaces can greatly simplify our work once we grasp their functionality.

Read more

Getting Started with Spring Security and JWT

Spring Security provides a comprehensive set of security features for Java applications, covering authentication, authorization, session management, and protection against common security threats such as CSRF (Cross-Site Request Forgery).

Read more

Creating and Publishing an NPM Package with Automated Versioning and Deployment

In this step-by-step guide, we’ll create, publish, and manage an NPM package using TypeScript for better code readability and scalability. We’ll write test cases with Jest and automate our NPM package versioning and publishing process using Changesets and GitHub Actions.

Read more