Java offers several built-in ways to generate random integers, from the classic Random class to newer, more targeted tools like ThreadLocalRandom and SecureRandom. Which one fits best depends on what you’re building — a simple game, a multithreaded app, or something that needs cryptographic-grade unpredictability.
Using the Random Class
The most common starting point is java.util.Random. This class lets you generate random numbers of different types — int, double, long, boolean — through methods like nextInt() for a full-range random integer and nextInt(bound) for a value between 0 inclusive and the bound exclusive.
import java.util.Random;
Random random = new Random();
int randomInt = random.nextInt(100) + 1;
Random numbers generated this way are unpredictable and unbiased, meaning every number in the possible range has an equal chance of coming up.
Generating a Number Within a Specific Range
Often you don’t just want any random integer — you want one between two bounds. A common pattern looks like this:
public int randomRangeInt(int start, int end) {
Random random = new Random();
int number = random.nextInt((end - start) + 1) + start;
return number;
}
Here, the bound value defines the upper limit of the range — for a range of 5 to 10, you’d pass 6 as the bound, since nextInt() generates a number from 0 up to but not including that bound, and adding the start value shifts it back into the desired range.
Using Math.random()
Another option that doesn’t require the Random class at all is Math.random(). This method returns a double greater than or equal to zero and less than one, and multiplying it by your desired range before casting to an int lets you scale it to whatever bounds you need. It’s a quick option for simple cases, though it takes a bit more manual math than using nextInt() directly.
Using ThreadLocalRandom for Concurrent Applications
If your code runs across multiple threads, plain Random isn’t ideal. ThreadLocalRandom.current().nextInt(min, max) generates a random integer within a specific range, with the min value inclusive and the max value exclusive — the same behavior as Random.nextInt(). It’s built specifically to avoid the contention issues that come from multiple threads sharing a single Random instance.
Using SecureRandom for Sensitive Applications
For anything security-related — tokens, encryption keys, OTPs — a standard Random instance isn’t strong enough. SecureRandom produces cryptographically secure random numbers and is essential for applications needing high-level security, such as encryption and secure token generation.
Generating a Stream of Random Integers
Java 8 introduced a way to generate multiple random values at once without looping manually.
- Fixed-size stream — ints(long streamSize) generates a stream of pseudorandom integers with a specified number of elements.
- Infinite stream — calling ints() with no arguments produces an infinite stream of random integers with no specific bounds.
- Bounded stream — ints(long streamSize, int randomNumberOrigin, int randomNumberBound) generates a stream of a set size, with each value falling between the given origin (inclusive) and bound (exclusive).
Excluding Specific Values
Sometimes you need randomness with a caveat — say, any number in a range except a few. You can filter a stream of random integers generated with Random.ints() against an exclusion array, returning the first value that doesn’t match any excluded number. This is handy for cases like assigning random IDs while avoiding ones already in use.
Join The Discussion
Random number generation comes up in everything from game logic to test data to security tokens, and the right tool really depends on the context. Which method do you reach for most in your own projects — plain Random, ThreadLocalRandom for concurrency, or SecureRandom when the stakes are higher — and have you run into any gotchas around bias or predictability worth sharing?