Until Java 7 there was no simple and effective way to generate random numbers concurrently. Here we show ThreadLocalRandom to generate random numbers in a thread-safe way.
One of the most notable additions in Java 7 was Fork Join Framework. But not many people know that it came with a bunch of classes useful in parallel computations. One of them is java.util.concurrent.ThreadLocalRandom. The class used in concurrent programs causes less overhead and contention than shared Random (which is not thread-safe as you probably know).
Thread safe random number generator
The usage of ThreadLocalRandom is pretty simple. We need to call current() static method to obtain a ThreadLocal version and then just obtain random numbers as usual. Here we use ints() from Java 8 to get stream of random ints and collect them into a List:
package com.farenda.java.util.concurrent; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import static java.util.stream.Collectors.toList; public class ConcurrentRandomNumbers { public List<Integer> randomRange(int from, int howMany) { return ThreadLocalRandom.current() .ints(howMany, from, from+howMany) .boxed() .collect(toList()); } }
In the below program we’re going to use this method from concurrently executed tasks.
Generating random numbers in ranges
Task executed concurrently
Simple task that will request random numbers in range:
package com.farenda.java.util.concurrent; import java.util.List; import java.util.concurrent.Callable; class ConcurrentRange implements Callable<List<Integer>> { private ConcurrentRandomNumbers generator; private final int from; private final int howMany; public ConcurrentRange(ConcurrentRandomNumbers generator, int from, int howMany) { this.generator = generator; this.from = from; this.howMany = howMany; } @Override public List<Integer> call() throws Exception { // request random numbers for this thread: return generator.randomRange(from, howMany); } }
Generate ranges of random numbers
We create three tasks to concurrently generate numbers in specified ranges and then just print the results:
package com.farenda.java.util.concurrent; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; public class ConcurrentRandomNumbers { private List<Integer> randomRange(int from, int howMany) { return ThreadLocalRandom.current() .ints(howMany, from, from+howMany) .boxed() .collect(toList()); } public static void main(String[] args) throws Exception { ConcurrentRandomNumbers generator = new ConcurrentRandomNumbers(); ExecutorService es = Executors.newCachedThreadPool(); List<ConcurrentRange> randomRanges = asList( new ConcurrentRange(generator, 10, 10), new ConcurrentRange(generator, 20, 10), new ConcurrentRange(generator, 30, 10)); List<Future<List<Integer>>> results = es.invokeAll(randomRanges); for (Future<List<Integer>> future : results) { System.out.println("Range: " + future.get()); } es.shutdown(); } }
Output of sample execution:
Range: [11, 16, 10, 10, 16, 13, 10, 11, 10, 18] Range: [24, 25, 26, 22, 26, 26, 24, 29, 27, 24] Range: [32, 30, 32, 30, 37, 31, 34, 30, 31, 36]