使用相同的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==' '));
}
}
}
答案 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();