来自随机和随机并发的IntStream

时间:2015-08-20 07:49:33

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

}

1 个答案:

答案 0 :(得分:8)

Random&#39; Javadoc说出来了:

  

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