并发使用 java.util.Random 时的争用
2022-09-04 02:24:05
java.util.Random 的实例是 threadsafe。但是,跨线程并发使用相同的 java.util.Random 实例可能会遇到争用,从而导致性能下降。请考虑在多线程设计中使用 ThreadLocalRandom。
性能不佳的原因可能是什么?
java.util.Random 的实例是 threadsafe。但是,跨线程并发使用相同的 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 通过每个线程有一个种子来确保种子更新而不会遇到任何争用。
随机类在内部状态周围保存一个同步锁,使得只有一个线程可以一次访问它 - 具体来说,它使用 .这意味着,如果您尝试使用多个线程从中读取它,则只有一个线程可以同时访问它,从而导致其他线程等待锁定被释放。AtomicLong
ThreadLocalRandom
可以改为使用来提供透明的每线程实例化,以确保内部状态基于每个线程进行更新,从而避免锁定。
请注意,如果实现正确,除非您运行大量线程,否则更新操作不应该执行得很糟糕,因为它基本上可以在JVM中优化到类似于x86。在锁之外,主要的计算成本可能是长乘法和旋转移位的组合。AtomicLong
lock xchg