来自随机和随机并发的IntStream

Dar*_*usz 11 java random concurrency java-8 java-stream

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

请考虑以下代码.这同样gen用于生成并行IntStream并且每几个字符生成一个随机空间.它成功运行并完成,没有异常抛出.

但这个代码线程安全吗?它似乎是,因为没有无效(超出范围)的字符值.我认为我应该破坏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==' '));
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Mar*_*nik 8

RandomJavadoc中阐明一下:

java.util.Random的实例是线程安全的.但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用并因此导致性能不佳.请考虑在多线程设计中使用ThreadLocalRandom.

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();
Run Code Online (Sandbox Code Playgroud)