并发使用java.util.Random时的争用

时间:2014-03-10 23:32:41

标签: java multithreading performance concurrency contention

Oracle Java documentation说:

  

java.util.Random的实例是线程安全的。但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用并因此导致性能不佳。请考虑在多线程设计中使用ThreadLocalRandom。

性能不佳背后的原因可能是什么?

2 个答案:

答案 0 :(得分:14)

在内部,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确保通过每个线程有一个种子来更新种子而不会遇到任何争用。

答案 1 :(得分:3)

随机类围绕内部状态保持同步锁定,这样只有一个线程可以一次访问它 - 具体来说,它使用AtomicLong。这意味着如果您尝试使用多个线程从中读取数据,则只有一个线程可以一次访问它,导致其他线程等待锁定被释放。

可以使用

ThreadLocalRandom来提供透明的每线程实例化,以确保在每个线程的基础上更新内部状态,从而避免锁定。

请注意,如果正确实施,AtomicLong更新操作不应该执行可怕,除非您运行大量线程,因为它基本上可以在JVM与x86上的lock xchg类似。锁定之外的主要计算成本可能是长乘法和旋转位移的组合。