在Java中,相对于简单的算术运算多长时间,Math.random()会生成一个数字?我试图在ArrayList中随机分配对象,这些对象已经包含了一些有点均匀但不完全均匀的分布,并且我不确定是否通过使用Math.random()为每个插入点选择一个随机索引最好的方法。
澄清:插入对象的分布应该足够均匀,以至于值不是全部集中在一个区域,而是也不均匀,分布不可预测(如果有人要去通过逐个值,他们将无法通过检测常量模式来确定下一个值是否将成为新插入的值。)
答案 0 :(得分:4)
不要使用Math.random。它依赖于java.util.Random
的全局实例,它使用了AtomicLong。虽然java.util.Random
中使用的PRNG算法是pretty simple,但性能主要受原子CAS和相关缓存一致性流量的影响。
对于多线程应用(如this example),这可能特别糟糕,但即使在单线程情况下也会受到惩罚。
ThreadLocalRandom始终优于Math.random。它不依赖于原子操作,也不会受到争用。它只会更新thread-local state并使用几个arithmetic and bitwise operations。
这是一个JMH基准,用于将Math.random()
和ThreadLocalRandom.current().nextDouble()
的性能与简单的算术运算进行比较。
package bench;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.ThreadLocalRandom;
@State(Scope.Thread)
public class RandomBench {
double x = 1;
@Benchmark
public double multiply() {
return x * Math.PI;
}
@Benchmark
public double mathRandom() {
return Math.random();
}
@Benchmark
public double threadLocalRandom() {
return ThreadLocalRandom.current().nextDouble();
}
}
结果表明,ThreadLocalRandom仅在几纳秒内工作,其性能可与简单的算术运算相媲美,并且在多线程环境中完美地扩展,与Math.random不同。
Benchmark Threads Score Error Units
RandomBench.mathRandom 1 34.265 ± 1.709 ns/op
RandomBench.multiply 1 4.531 ± 0.108 ns/op
RandomBench.threadLocalRandom 1 8.322 ± 0.047 ns/op
RandomBench.mathRandom 2 366.589 ± 63.899 ns/op
RandomBench.multiply 2 4.627 ± 0.118 ns/op
RandomBench.threadLocalRandom 2 8.342 ± 0.079 ns/op
RandomBench.mathRandom 4 1328.472 ± 177.216 ns/op
RandomBench.multiply 4 4.592 ± 0.091 ns/op
RandomBench.threadLocalRandom 4 8.474 ± 0.157 ns/op
答案 1 :(得分:2)
Java文档将此报告为Random.nextDouble()
的实现,这是Math.random()
最终调用的内容。
public double nextDouble() {
return (((long)next(26) << 27) + next(27))
/ (double)(1L << 53);
}
下一步将种子更新为(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
并返回(int)(seed >>> (48 - bits))
。
正如您所看到的,它使用一种简单的算法来生成伪随机值。它只需要一些便宜的操作,因此我不担心使用它。
答案 2 :(得分:1)