【问题标题】:Seed a random generator without time in java cross-plateform-ably在java中跨平台播种一个没有时间的随机生成器
【发布时间】:2013-01-26 02:55:29
【问题描述】:

我几乎同时在两个线程上初始化两个随机数生成器,我希望这两个生成器的行为完全不同。我会经常在两台发电机上一个接一个地打电话给Random.nextInt(7)。使用System.currentTimeMillis() 不是一个好主意,因为看起来我的计算机速度非常快,以至于我从两个生成器获得的数字很有可能是相同的。那么有什么方法可以配置Random,以便尽管它们被一个接一个地调用,但它们的行为仍然不同?我希望解决方案能够跨平台兼容,因此任何特定于平台的想法(例如从/dev/random 读取)都是不可接受的。感谢您的帮助。

【问题讨论】:

    标签: java random


    【解决方案1】:

    您可以使用SecureRandom 为您的两个随机数生成器生成种子。它使用可以使用特定于平台的熵源的服务提供商基础架构,因此在可用的系统上,您可能会从/dev/random 获得熵,而不必担心代码中的熵。 another answer中提到的UUID就是从这样一个来源生成的,至少是in OpenJDK 7.6

    也就是说,请注意,从 Java 7 开始,在多线程中使用 PRNG 的首选方式是 ThreadLocalRandom。我不确定,但在我看来,这里的主要目标是避免同步开销,而不是播种问题。至少在 OpenJDK 7.6 中,constructor 使用default Random constructor,后者又使用高分辨率系统时间(以纳秒为单位,但不一定与实际分辨率相结合),并结合静态变量的当前值。后者即使对于在系统时钟的同一滴答声中创建的实例也能确保不同的种子,因此即使使用默认构造函数简单地构造 Random 实例,您的原始问题也应该消失。

    这个唯一性是 2010 年的 introduced,以响应 bug report #6937857。该报告针对 Java 7 进行了报告,并且在 Java 7 中也已修复,但根据 mercurial 存储库,此更改应该首先包含在 jdk7-b94 release 中。

    【讨论】:

    • 适用于 Oracle java 1.6 的解决方案会更有价值。
    • @AlexWien:SecureRandom 的服务提供者接口源自 Java 1.2。我没有这方面的资料,所以我不知道他们在幕后到底在使用什么,但我的 Oracle Java 1.6 附带了一个 java.security 文件,其中包含 securerandom.source=file:/dev/urandom 行。正如我所预料的那样,我的第一段也适用于 Java 1.6。
    【解决方案2】:

    什么都不做。它有效!

    这是java.util.Random的代码:

    public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }
    

    顾名思义,seedUniquifier 使种子独一无二。它可以在单线程和多线程中工作:

    private static long seedUniquifier() {
        // L'Ecuyer, "Tables of Linear Congruential Generators of
        // Different Sizes and Good Lattice Structure", 1999
        for (;;) {
            long current = seedUniquifier.get();
            long next = current * 181783497276652981L;
            if (seedUniquifier.compareAndSet(current, next))
                return next;
        }
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用System.nanoTime(),而不是System.currentTimeMillis()

      只需将第二个线程的创建延迟 30ms,这将导致 nanoTime() 与 thread1 的 nanoTime() 随机不同

      所以你的问题有一个实用的观点,对于这个观点 nanoTime() 我认为足够了。

      另一种观点,是军事安全加密的更理论化的观点。在那里,您想使用使用物理随机性(随机寄存器)的特殊硬件。但这不是你的意图。

      【讨论】:

      • 仍然有可能(尽管减少了)相同的起始种子。
      • 我不这么认为,nano time不是一个绝对时间,它是以nano seconds为单位的处理器启动后的时间,当你的系统启动时,它访问硬盘,稍微有点机械随机存取速度。所以当你的java程序启动时,nanoTime是随机的。
      • Matt 正在谈论 2 个线程非常接近地调用 get nanoTime() 并接收相同的时间......
      • @MitchWheat 是的,然后只需创建两个 Random() 实例,启动一个线程并创建种子 = nanos 的随机对象,然后等待 100 毫秒,然后创建下一个种子 = nano 的随机对象; 100ms 从来都不是精确的,所以它也是随机的。准备好了。
      • 有点违背了目的。您可以将相同的逻辑应用于海报原始场景,只是等待时间更长......
      【解决方案4】:

      您可以使用System.nanoTime(),这将减少两个线程具有相同种子的可能性。

      或者创建一个线程安全的实用程序类来为您获取随机数对象,同时确保它为每个新实例使用不同的种子。

      此外,您可以为每个线程分配一个 数字 ID 作为线程名称并将其附加到种子

      【讨论】:

        【解决方案5】:

        一种方法:使用 UUID (GUID) 为每个 Random 实例播种,该 UUID (GUID) 已转换(散列?但可能不仅仅是转换)为 long。

        其他答案建议使用nanoTime,根据硬件速度可能合适,但我更喜欢UUID路由。

        【讨论】:

        • 什么是 GUID ?从哪里得到?
        猜你喜欢
        • 1970-01-01
        • 2016-07-22
        • 1970-01-01
        • 2021-02-22
        • 2019-04-20
        • 2010-10-05
        相关资源
        最近更新 更多