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