java.util.Random 的实例是线程安全的。但是,跨线程并发使用同一个 java.util.Random 实例可能会遇到争用,从而导致性能下降。考虑改为在多线程设计中使用 ThreadLocalRandom。
表现不佳的原因可能是什么?
java.util.Random 的实例是线程安全的。但是,跨线程并发使用同一个 java.util.Random 实例可能会遇到争用,从而导致性能下降。考虑改为在多线程设计中使用 ThreadLocalRandom。
表现不佳的原因可能是什么?
在内部,java.util.Random 与当前种子保持一个 AtomicLong,并且每当请求新的随机数时,都会在更新种子时发生争用。
从 java.util.Random 的实现来看:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
另一方面,ThreadLocalRandom 通过每个线程有一个种子来确保更新种子而不会面临任何争用。
random 类在内部状态周围持有一个同步锁,这样只有一个线程可以一次访问它——具体来说,它使用AtomicLong
. 这意味着如果您尝试使用多个线程从中读取,则只有一个线程可以一次访问它,导致其他线程等待直到锁被释放。
ThreadLocalRandom
可用于提供透明的每线程实例化,以确保在每线程的基础上更新内部状态,从而避免锁定。
请注意,如果正确实施,除非您运行大量线程,否则AtomicLong
更新操作不应该lock xchg
执行得很糟糕,因为它基本上可以在 JVM 中优化到类似于x86 上的东西。锁之外的主要计算成本可能是长乘法和旋转移位的组合。