Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IntStream from Random and Random concurrency

Is it safe to use the same Random instance to generate a stream (or parallel stream) and to influence that stream in one of its parts?

Consider the code below. The same gen is used to generate a parallel IntStream and to generate a random space every few chars. It runs and completes successfully, no exception thrown.

But is this code thread safe? It appears it is, because there are no invalid (out of range) character values. I think I should be corrupting Random's internal data, since its methods are not marked as synchronized, but apparently that's not the case. Why?

public class RandomGenTest {

    Random gen = new Random();

    String getRandomText(int len, double spaceProb) {
        return gen.ints(len, 'a', 'z'+1)
                    .map(i-> gen.nextDouble()<spaceProb?' ':i)
                    .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
    }

    @Test
    public void test() {
        for (int a=10000; a<10000000; a*=2) {
            String text = getRandomText(a, .2);
            Assert.assertTrue(text.chars().allMatch(c -> (c>='a' && c<='z') || c==' '));
        }
    }

}
like image 916
Dariusz Avatar asked Aug 20 '15 07:08

Dariusz


1 Answers

Random's Javadoc spells it out:

Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using ThreadLocalRandom in multithreaded designs.

Random is a thread-safe object by virtue of an AtomicLong which keeps the current seed, so using it reverses most of the parallel speedup which is the point of your exercise.

Instead use ThreadLocalRandom.getCurrent() and avoid at least the contention issue (although by introducing the overhead of ThreadLocal lookup). Also use the SplittableRandom to retrieve the outer stream of random numbers. This implementation allows random access to stream elements, which is key to goood parallelizability.

import static java.util.concurrent.ThreadLocalRandom.current;

String getRandomText(int len, double spaceProb) {
    return new SplittableRandom().ints(len, 'a', 'z'+1).parallel()
      .map(i -> current().nextDouble()<spaceProb ? ' ' : i)
      .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
like image 134
Marko Topolnik Avatar answered Nov 16 '22 23:11

Marko Topolnik