Oracle Java Documentation says:
Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using ThreadLocalRandom in multithreaded designs.
穷人表现背后的原因是什么?
大概是因为内部状态有某种同步?
在内部,java.util.random使用当前种子保持原子长度,并且每当请求一个新的随机数时,在更新种子时就会发生争用。
从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通过每个线程有一个种子来确保在不面临任何争用的情况下更新种子。
这也意味着只有在频繁地调用next时才会出现性能差的情况,因为如果更多的线程同时调用next,则compareAndSet会更频繁地失败。
随机类围绕内部状态持有一个同步锁,这样只有一个线程可以一次访问它——具体来说,它使用AtomicLong。这意味着,如果试图用多个线程从中读取,那么只有一个线程可以同时访问它,从而导致其他线程等待直到释放锁。
可以使用ThreadLocalRandom来提供每个线程实例的透明性,以确保在每个线程的基础上更新内部状态,从而避免锁定。
请注意,如果正确实现,除非运行大量线程,否则AtomicLong更新操作不应执行得很糟糕,因为它基本上可以在JVM中优化为类似于x86上的lock xchg之类的操作。锁之外的主要计算成本可能是长乘法和旋转移位的组合。
这不对。多个线程可以同时访问一个atomiclong。访问没有锁定。同时存在的原子使用挥发物。
不正确,Random遭受争用的原因是AtomicLong.compareAndSet,如果与多个线程一起使用,它本身就会受到争用。请参阅stackoverflow.com/questions/3556283/…