IntStream 来自 Random 和 Random 并发

IntStream from Random and Random concurrency

使用相同的 Random 实例来生成流(或并行流)并影响其中一部分的流是否安全?

考虑下面的代码。相同的 gen 用于生成并行 IntStream 并每隔几个字符生成随机 space。它运行并成功完成,没有抛出异常。

但是这段代码线程安全吗?看起来是这样,因为没有无效(超出范围)的字符值。我想我应该破坏 Random 的内部数据,因为它的方法没有标记为 synchronized,但显然情况并非如此。为什么?

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==' '));
        }
    }

}

RandomJavadoc拼写出来:

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 是一个线程安全对象,因为 AtomicLong 保留当前种子,因此使用它可以逆转大部分并行加速,这是您练习的重点。

而是使用 ThreadLocalRandom.getCurrent() 并至少避免了争用问题(尽管引入了 ThreadLocal 查找的开销)。还可以使用 SplittableRandom 检索外部随机数流。此实现允许随机访问流元素,这是实现良好并行化的关键。

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();