【问题标题】:Random over ThreadLocalRandomThreadLocalRandom 上的随机
【发布时间】:2014-06-17 05:49:36
【问题描述】:

java.util.Random 的实例是线程安全的。然而,并发 跨线程使用相同的 java.util.Random 实例可能会遇到 争用和随之而来的糟糕表现。考虑改为使用 多线程设计中的 ThreadLocalRandom。

什么样的争用导致性能不佳?有人可以在这里解释一下吗?我不知道 Random 和 ThreadLocalRandom 中的什么算法使它们不同。

【问题讨论】:

标签: java multithreading random


【解决方案1】:

希望,这可能会有所帮助-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。

为了更好地学习:

【讨论】:

    【解决方案2】:

    这可能会有所帮助:

    http://thoughtfuljava.blogspot.com/2012/09/prefer-threadlocalrandom-over-random.html


    引自来源:

    通常要生成随机数,我们要么创建java.util.Random 的实例,要么创建Math.random() 的实例——它在第一次调用时在内部创建java.util.Random 的实例。但是,在并发应用程序中使用上述内容会导致争用问题。

    Random 是线程安全的,可供多个线程使用。但如果多个线程使用Random 的同一个实例,则多个线程共享同一个种子。它会导致多个线程之间的争用,从而导致性能下降。

    ThreadLocalRandom 是上述问题的解决方案。 ThreadLocalRandom 每个线程都有一个Random 实例,可以防止争用。


    因此,基本上,每个线程使用一个随机实例可以让您停止同步必须由所有线程使用的种子。

    【讨论】:

    • 如果我在每个线程中创建一个 java.util.Random 的新实例,它不会导致与在每个线程中使用 ThreadLocalRandom 相同的效果吗?还是 java.util.Random 的所有实例都使用相同的种子?
    • Random 的部分问题在于它是不必要的synchronized;即使您为每个线程创建一个,它的性能也不会像ThreadLocalRandom那样。
    • 对于多次调用 SecureRandom 的 32 线程应用程序...测得,每个线程使用一个实例与应用程序使用一个(静态)实例相比,性能至少提高了 4 倍。
    【解决方案3】:

    ThreadLocalRandom 有一些问题,你不能 控制初始种子。我也没有在某处找到工作集种子方法。

    应该注意的是,当多个线程使用 Math.random() 时会发生争用,因为它们会在后台访问共享实例 在类 Random 中,使用 ThreadLocalRandom 有另一种选择 这也解决了种子问题。

    ThreadLocalRandom 使用隐藏在线程中的种子。他们决定 为你做最初的种子,没有任何手段可以控制它。你 同样可以创建您自己的 Random 实例并将其用于 线程本地时尚。因此,如果您执行以下操作:

    /* my thread */
    rnd = new Random(my_seed);
    /* use rnd */
    

    您也不会看到任何争用。并且使用相同的种子,您将获得可重现 随机序列,有助于测试。当你有多个线程时 您可以通过这些线程分发种子。应该有算法来生成良好的距离种子。

    【讨论】:

      【解决方案4】:

      核心算法基本相同。 ThreadLocalRandom 使用 Java ThreadLocal 构造为每个线程创建一个新的 Random 变量。这保证了来自每个线程的调用永远不会与每个线程发生冲突(无争用)。

      看看 Random 中的这一行进行比较:

        } while (!seed.compareAndSet(oldseed, nextseed));
      

      当您要求下一个值时,Random 会采用旧值并生成一个新值。然后,它使用 AtomicLong.compareAndSet 函数设置新值,前提是旧值仍然是它使用的值。如果另一个线程更改了该值,则循环将再次运行(并再次运行,直到它成为在一次随机数生成中同时获取和设置值的唯一循环)。因此可能存在争用,从而可能影响性能。

      ThreadLocalRandom,因为保证不冲突,所以不需要原子函数和线程安全操作/锁定。

      您需要考虑一些权衡。使用一个 Random 允许一个随机数生成器,如果您想为您的应用程序使用单个种子,这非常有用。如果您只是偶尔调用 Random,因此冲突可能是“罕见的”(不是正常情况),那么您可能不必担心冲突,并且个人对性能的小影响可能无关紧要。如果您在多个线程中每秒调用数百次随机,那么您显然希望使用 ThreadLocalRandom。

      【讨论】:

        【解决方案5】:

        一个 Random 实例一次只能向一个线程提供一个随机数。因此,如果您有多个线程同时从该实例请求随机数,这往往会减慢所有线程的速度。

        另一方面,每个线程都有自己的 ThreadLocalRandom 实例,因此在请求随机数时不会阻塞任何线程。

        【讨论】:

          【解决方案6】:

          来自 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.
          

          Random 可以多次创建/同一个 Random 对象将在多个线程之间共享(因为使用安全)。如何通过多个线程创建多个实例/相同的资源访问会导致开销。

          而不是为每个线程创建实例并在 ThreadLocal 中维护资源会更完美。因为实例不是跨多个线程共享的。并且没有公共构造函数,你应该使用工厂方法来获取它。

          我会说,它只是随机对象工厂,它为每个线程维护 /caches 实例。

          【讨论】:

            【解决方案7】:

            好吧,如果您在多个线程上使用相同的数据结构,它通常必须同步。这是昂贵的,需要时间。 ThreadLocalRandom 不必同步,因为它只由一个线程使用。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-02-11
              • 2015-03-08
              • 1970-01-01
              • 2012-02-21
              • 1970-01-01
              相关资源
              最近更新 更多