【问题标题】:Should I prefer ThreadLocalRandom over ThreadLocal<Random>?我应该更喜欢 ThreadLocalRandom 而不是 ThreadLocal<Random>?
【发布时间】:2013-04-02 13:28:48
【问题描述】:

我希望在多个线程中使用(种子)Random 对象,javadocs 将我指向ThreadLocalRandom,这看起来很棒,除了我无法设置种子,所以我无法确保不同线程之间的一致性线程或运行。是否有任何实际理由使用ThreadLocalRandom 或者可以接受如下操作:

// Pass returned ThreadLocal object to all threads which need it
public static ThreadLocal<Random> threadRandom(final long seed) {
    return new ThreadLocal<Random>(){
        @Override
        protected Random initialValue() {
            return new Random(seed);
        }
    };
}

【问题讨论】:

  • 你想在不同线程之间共享一个 Random 实例吗?或者确保每个线程都有自己的 Random 实例?
  • 根据Random 在线程之间共享一个Random 对象的文档是一个糟糕的主意。我只想控制正在使用的随机对象的种子,以便行为一致且可重复。
  • 您可以通过反射注入种子(将初始化设置为 false,调用 setSeed 并将初始化设置回 true)。
  • 通常情况下,在使用多个线程时不存在一致性。您的解决方案很好,只是比ThreadLocalRandom 慢一点,尤其是在JDK8 中。

标签: java random concurrency


【解决方案1】:

您可以简单地使用Random,只需确保每个Random 对象只能在单个线程中访问。

Random,像Vector 这样的古老类,不必要地高度同步。他们可能想炫耀 Java 的线程支持,因为这在当时很重要。此外,Java 主要用于在大多数具有单处理器的消费类 PC 上运行,因此同步不会像现在在多处理器上那样影响扩展。

现在一个明显的答案是提供Random 的线程不安全版本,就像提供线程不安全的ArrayList 作为Vector 的替代品一样。这并没有发生,相反,我们得到了ThreadLocalRandom。这有点奇怪,不知道背后的动机是什么。在java8中,ThreadLocalRandom被进一步优化,可以直接对Thread对象中的一些int字段进行操作。

【讨论】:

    【解决方案2】:

    code for ThreadLocalRandom 似乎被实现为ThreadLocal 无论如何(不完全像你说的那样,但可能足够接近)。我认为你所拥有的会很好用。

    【讨论】:

    • 1.在 Java 8 中,Thread 中为 ThreadLocalRandom 指定了字段,因此不再使用 ThreadLocal。 2. 虽然使用 ThreadLocal 确实消除了争用,但在 Random 中仍然进行了不必要的检查。因此它的性能无法与 ThreadLocalRandom 相比(即使在 Java 7 中)。
    • @kavinyao 在 Java 中的无竞争同步现在相当快。
    【解决方案3】:

    首先,您的代码为每个线程使用相同的种子。这可能是一个安全问题。 此外,对 Random 实例的成员的每次访问都是同步的,因此它会比 ThreadLocalRandom 的相应方法(不同步)慢。 如果编译器可以确定您的 Random 实例没有逃脱当前线程,它可以优化那些“同步”的东西。但是由于您可以将共享引用存储在 ThreadLocal 中,因此编译器无法检查这一点。

    【讨论】:

      【解决方案4】:

      取决于所需种子的性质,ThreadLocalRandom 不允许设置随机种子。在某些情况下,您可能希望通过当前请求中的“用户”来播种 Random,因此“随机性对于用户而言是一致的”,并且容器会为新请求和新 Random 重用线程(很少) () 很昂贵,您可能想创建自己的 ThreadLocal。由于JavaThreadLocalRandom不支持种子,你可以在JDK 1.8中轻松编写:

      public class RandomUtil {
      
       private static final ThreadLocal<Random> RANDOM_THREAD_LOCAL = ThreadLocal.withInitial(Random::new);
      
       public static Random threadLocalRandom(long seed){
          Random random = RANDOM_THREAD_LOCAL.get();
          random.setSeed(seed);
          return random;
      }
      

      由于每个 Random 都是线程本地的并且线程安全,因此可以“如果需要”为下一个请求/用户重新播种,而无需创建新的 Random()。这应该比为每个请求创建 new Random(user seed) 更高效,但比具有内部优化的 Java ThreadLocalRandom 慢。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-30
        • 1970-01-01
        • 2011-04-01
        • 2022-01-08
        • 2017-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多