Java 在多线程环境中配置随机数生成器
Java Configuring random number generators in multi-threaded environment
我有一个使用伪随机数的应用程序。当我测试应用程序时,我想要再现性,我通过使用随机数生成器的种子来安排再现性。目前,RNG 在 class 中声明,将其用作静态变量:private static Random rng = new Random(seed)
。我有几个问题。
- 鉴于我想为 RNG 播种以实现可重复性,声明变量
static
是否有意义?
- 我可能需要添加更多需要随机数的不同 classes。我是否应该将 RNG 移动到单独的 class 以便我仍然可以为整个应用程序使用一个种子?如果是这样,我应该使用
static Random
还是单例对象?
- 如果我决定在多个线程中使用随机数会怎样?似乎如果我使用像 2. 这样的策略,我可能仍然会失去可预测性,因为我不能确定线程访问 RNG 的顺序。 RNG 仍然会生成相同的、可预测的随机数序列,但是从程序的一个 运行 到另一个,获取序列中第一个(或第 n 个)随机数的线程可能不同。
- 这向我表明,每个线程都需要自己的 RNG,以便在所有 运行 中重现结果。这是否意味着每个线程都需要自己的种子?如果是这样,是否可以保证再现性,或者我是否需要做其他事情来消除多线程可能引入的随机性?
- 有没有一种方法可以从单个数字生成种子,从而最大限度地减少不同 RNG 之间的相关性?
换句话说,如果线程0有Random thread0RNG = new Random(seed)
,线程1有Random thread1RNG = new Random(seed)
,我只需要一个种子,但每个线程中的随机数会高度相关。我可以有两个种子,但是我无法将程序的 运行 与单个数字相关联,例如在命令行上传递。说 seed0 = someFunction(seed,0)
和 seed1 = someFunction(seed,1)
是否可能且合适?
是的,您应该为每个线程提供自己的 Random 对象,以避免由于线程时序导致的不可重现的交错。
您可以用相同的数字为他们做种子,但随后他们都会得到完全相同的数字序列。不知道有没有问题。
如果有问题,您可以使用一个 "master" 随机对象来为其他对象生成种子。每次创建新线程时,从主线程获取种子。但是您必须确保每次都以相同的顺序创建所有线程(或至少检索种子)。
为每个线程使用一个 RNG,例如 ThreadLocalRandom。如果每个线程都有自己的生成器,多线程不会影响随机性。种子的选择由你决定。
首先,Random
是线程安全的,因此您可以安全地跨多个线程使用它,尽管由于锁定,单个 Random
实例可能会导致性能不佳。为避免这种情况,请改用 ThreadLocalRandom
。
但这不是解决方案,如果像您的情况一样需要可预测性。在并发环境中拥有一个共享的 RNG 永远是不可预测的。因此你是对的,你需要每个线程一个实例,并且每个实例都必须用种子初始化。
但是如果所有的RNG实例都使用相同的种子,那么生成的随机序列不仅会高度相关,而且会完全相同!所以你必须为所有 RNG 使用不同的种子
所以我建议使用中央 RNG 为所有其他 RNG 生成种子。但请注意,这只是可预测的,前提是 RNG 的播种顺序定义明确 - 同样,这在并发环境中可能不是直截了当的,但可行。
我有一个使用伪随机数的应用程序。当我测试应用程序时,我想要再现性,我通过使用随机数生成器的种子来安排再现性。目前,RNG 在 class 中声明,将其用作静态变量:private static Random rng = new Random(seed)
。我有几个问题。
- 鉴于我想为 RNG 播种以实现可重复性,声明变量
static
是否有意义? - 我可能需要添加更多需要随机数的不同 classes。我是否应该将 RNG 移动到单独的 class 以便我仍然可以为整个应用程序使用一个种子?如果是这样,我应该使用
static Random
还是单例对象? - 如果我决定在多个线程中使用随机数会怎样?似乎如果我使用像 2. 这样的策略,我可能仍然会失去可预测性,因为我不能确定线程访问 RNG 的顺序。 RNG 仍然会生成相同的、可预测的随机数序列,但是从程序的一个 运行 到另一个,获取序列中第一个(或第 n 个)随机数的线程可能不同。
- 这向我表明,每个线程都需要自己的 RNG,以便在所有 运行 中重现结果。这是否意味着每个线程都需要自己的种子?如果是这样,是否可以保证再现性,或者我是否需要做其他事情来消除多线程可能引入的随机性?
- 有没有一种方法可以从单个数字生成种子,从而最大限度地减少不同 RNG 之间的相关性?
换句话说,如果线程0有Random thread0RNG = new Random(seed)
,线程1有Random thread1RNG = new Random(seed)
,我只需要一个种子,但每个线程中的随机数会高度相关。我可以有两个种子,但是我无法将程序的 运行 与单个数字相关联,例如在命令行上传递。说 seed0 = someFunction(seed,0)
和 seed1 = someFunction(seed,1)
是否可能且合适?
是的,您应该为每个线程提供自己的 Random 对象,以避免由于线程时序导致的不可重现的交错。
您可以用相同的数字为他们做种子,但随后他们都会得到完全相同的数字序列。不知道有没有问题。
如果有问题,您可以使用一个 "master" 随机对象来为其他对象生成种子。每次创建新线程时,从主线程获取种子。但是您必须确保每次都以相同的顺序创建所有线程(或至少检索种子)。
为每个线程使用一个 RNG,例如 ThreadLocalRandom。如果每个线程都有自己的生成器,多线程不会影响随机性。种子的选择由你决定。
首先,Random
是线程安全的,因此您可以安全地跨多个线程使用它,尽管由于锁定,单个 Random
实例可能会导致性能不佳。为避免这种情况,请改用 ThreadLocalRandom
。
但这不是解决方案,如果像您的情况一样需要可预测性。在并发环境中拥有一个共享的 RNG 永远是不可预测的。因此你是对的,你需要每个线程一个实例,并且每个实例都必须用种子初始化。
但是如果所有的RNG实例都使用相同的种子,那么生成的随机序列不仅会高度相关,而且会完全相同!所以你必须为所有 RNG 使用不同的种子
所以我建议使用中央 RNG 为所有其他 RNG 生成种子。但请注意,这只是可预测的,前提是 RNG 的播种顺序定义明确 - 同样,这在并发环境中可能不是直截了当的,但可行。