java.util.Random的实例是线程安全的。但是,并发 跨线程使用相同的java.util.Random实例可能会遇到 争用和随之而来的糟糕表现。考虑改为使用 多线程设计中的ThreadLocalRandom。
什么样的争用,从而表现不佳?有人可以,在这里解释一下吗?我不知道Random和ThreadLocalRandom里面的算法是什么让它们变得不同。
答案 0 :(得分:36)
这可能会有所帮助:
http://thoughtfuljava.blogspot.com/2012/09/prefer-threadlocalrandom-over-random.html
引自来源:
通常要生成随机数,我们要么做 创建java.util.Random OR的实例 Math.random() - 在第一次调用时在内部创建java.util.Random的实例。但是,在并发应用程序中,上述使用会导致争用问题
Random是多线程使用的线程安全的。但是如果多个线程使用相同的Random实例,则多个线程共享相同的种子。它会导致多个线程之间的争用,从而导致性能下降。
ThreadLocalRandom解决了上述问题。 ThreadLocalRandom每个线程都有一个Random实例,可以防止争用。
所以,基本上,每个线程使用一个随机实例可以让你停止同步所有线程必须使用的种子。
答案 1 :(得分:4)
ThreadLocalRandom存在一些问题,你不能这样做 控制初始种子。我也找不到某处的工作集种子方法。
应该注意的是,当多个线程使用Math.random()时存在争用,因为它们将在底层访问共享实例 在Random类中,有一个使用ThreadLocalRandom的替代方法 这也解决了种子问题。
ThreadLocalRandom使用存储在Thread中的种子。他们决定了 为你做初始种子,没有任何手段来控制它。您 同样可以创建自己的Random实例并在a中使用它 线程本地时尚。因此,如果您执行以下操作:
/* my thread */
rnd = new Random(my_seed);
/* use rnd */
您也会看到没有争用。使用相同的种子,您可以获得可重现性 随机序列,可以帮助测试。当你有多个线程 你可以通过这些线程分发种子。应该有算法来生成良好的距离种子。
答案 2 :(得分:2)
核心算法基本相同。 ThreadLocalRandom使用Java ThreadLocal构造为每个线程创建一个新的Random变量。这保证了每个线程的调用永远不会与每个线程冲突(没有争用)。
从Random中查看这一行以进行比较:
} while (!seed.compareAndSet(oldseed, nextseed));
当您要求下一个值时,Random会获取旧值并生成一个新值。然后它使用AtomicLong.compareAndSet函数来设置新值,仅当旧值仍然是它使用的值时。如果另一个线程更改了该值,则循环将再次运行(并且再次运行,直到它是唯一的循环,它们都获得并在一个随机数生成中设置该值)。因此可能存在争用,从而可能影响性能。
ThreadLocalRandom因为保证不会发生冲突,所以不需要原子函数和线程安全操作/锁定。
您需要考虑一些权衡。使用一个Random允许一个随机数生成器,如果您想为您的应用程序使用单个种子,这非常有用。如果您只是偶尔拨打随机电话,那么冲突很可能是“罕见的”(不是正常情况),那么您可能不会担心冲突,并且对性能的个别影响可能无关紧要。如果你在多个线程中每秒调用几百个随机时间,那么你显然想要使用ThreadLocalRandom。
答案 3 :(得分:1)
好吧,如果你在多个线程上使用相同的数据结构,通常需要进行同步。这很昂贵,需要时间。 ThreadLocalRandom不必同步,因为它仅由一个线程使用。
答案 4 :(得分:0)
来自ThreadLocalRandom API文档
A random number generator isolated to the current thread. Like the
* global {@link java.util.Random} generator used by the {@link
* java.lang.Math} class, a {@code ThreadLocalRandom} is initialized
* with an internally generated seed that may not otherwise be
* modified. When applicable, use of {@code ThreadLocalRandom} rather
* than shared {@code Random} objects in concurrent programs will
* typically encounter much less overhead and contention. Use of
* {@code ThreadLocalRandom} is particularly appropriate when multiple
* tasks (for example, each a {@link ForkJoinTask}) use random numbers
* in parallel in thread pools.
可以多次创建随机/相同的随机对象将在多个线程之间共享(由于使用的事实是安全的)。如何通过多个线程创建多个实例/相同资源访问将导致开销。
而不是为每个线程创建实例并在ThreadLocal中维护资源会更完美。因为实例不是跨多个线程共享的。并且没有公共构造函数,您应该使用工厂方法来获取它。
我想说,它只是随机对象的工厂,它维护/缓存每个线程的实例。
答案 5 :(得分:0)
随机实例一次只能为一个线程提供一个随机数。因此,如果您有多个线程同时从该实例请求随机数,则往往会减慢所有线程的速度。
另一方面,每个线程都有自己的ThreadLocalRandom实例,因此在请求随机数时不会阻塞任何线程。
答案 6 :(得分:0)
希望,这可能会有所帮助-Top 4 ways to Generate Random Numbers in Java
随机
Java Random 类用于生成伪随机数流。 Random 类实现的算法使用受保护的实用程序方法,每次调用最多可以提供 32 个伪随机生成的位。
doubles()
:返回无限制的伪随机双精度值流。ints()
:返回无限的伪随机 int 值流。longs()
:返回无限长的伪随机长值流。next()
:生成下一个伪随机数。ThreadLocalRandom:
这个随机数生成器与当前线程隔离。与 Math 类使用的全局 Random 生成器一样,ThreadLocalRandom 使用内部生成的种子初始化,否则可能无法修改。
在并发程序中使用 ThreadLocalRandom 通常会比使用全局 Random 类遇到更少的开销和争用。
当 ForkJoinTask 等多个任务需要在线程池中并行处理随机数时,您可以使用 ThreadLocalRandom。
为了更好地学习: