Java 来自随机和随机并发的IntStream
使用相同的Java 来自随机和随机并发的IntStream,java,random,concurrency,java-8,java-stream,Java,Random,Concurrency,Java 8,Java Stream,使用相同的Random实例生成流(或并行流)并在其中一个部分影响该流是否安全 考虑下面的代码。相同的gen用于生成并行IntStream,并每隔几个字符生成一个随机空间。它成功运行并完成,没有引发异常 但这段代码是线程安全的吗?看起来是这样,因为没有无效(超出范围)的字符值。我认为我应该破坏Random的内部数据,因为它的方法没有标记为synchronized,但显然不是这样。为什么? public class RandomGenTest { Random gen = new Rand
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==' '));
}
}
}
公共类随机测试{
Random gen=新的Random();
字符串getRandomText(int len,双空格prob){
返回gen.ints(长度'a',z'+1)
.map(i->gen.nextDouble()='a'和&c随机
Auv.Read的实例是线程安全的。但是,跨线程同时使用相同的JavaUTIL随机实例可能会遇到争用,从而导致性能不佳。
Random
是一个线程安全的对象,因为AtomicLong
保留了当前种子,因此使用它可以逆转大部分并行加速,这是您练习的重点
相反,请使用ThreadLocalRandom.getCurrent()
并至少避免争用问题(尽管引入了ThreadLocal
查找的开销)。还可以使用SplittableRandom
检索外部随机数流。此实现允许对流元素进行随机访问,这是good并行化的关键
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();
导入静态java.util.concurrent.ThreadLocalRandom.current;
字符串getRandomText(int len,双空格prob){
返回新的SplittableRandom().int(len'a',z'+1).parallel()
.map(i->current().nextDouble())请注意,在并行流上使用limit
会导致性能灾难。在这里没有必要,因为您可以首先使用它来构造有限流。此外,通过new Character
显式创建新对象是没有必要的,您可以让自动装箱做正确的事情。但是当您更改首先StringBuilder::append
到StringBuilder::appendCodePoint
,您根本不需要装箱,只需使用IntStream
即可执行所有操作。顺便说一句,绑定是独占的,因此如果您希望包含'z'
,则必须使用'z'+1
作为绑定。→ <代码>gen.ints(len,'a','z'+1).parallel().map(i->gen.nextDouble())顺便说一下,您还可以简化断言:Assert.assertTrue(text.chars().allMatch(c->='a'&&c@Holger,限制()在这里不是灾难,因为gen.ints('a','z')
返回一个无序流。gen.ints(1_000_000,'a','z'+1)。parallel().toArray()
对我来说显示的性能与gen.ints('a','z'+1).parallel().limit(1_000_000).toArray()一样差
。真正的问题是使用Random
而不是SplittableRandom
@Tagir Valeev:Random number generation通常不是一个便宜的操作,但我想强调的是,程序员应该避免在并行流上使用limit
,特别是在有一个简洁的替代方法来构建一个fi的情况下首先是一条非常大的河流。它是关于(不)习惯它…