bigywater.tech
Java · 382

测试数据生成:随机不等于不可复现

随机的测试数据不等于不可复现的测试数据——前提是你控制了种子、线程与派生方式。这篇逐项量出:哪些随机源能设种子,种子跨 JDK 是否稳定,共享随机数在多线程与并行流里为什么会丢失可复现性,以及不能拿什么当种子。

3 个 JDK
Random / SplittableRandom / L64X128MixRandom / shuffle 的同种子输出逐字相同(17、21、27)
6 种
8 线程共享一个 Random(42) 时 6 次运行得到的各线程取值组合数(总和都是 3984002)
1 种 vs 5 种
并行流 5 次运行:按下标派生种子的结果哈希种数 vs 共享 Random 的哈希种数

同种子跨 JDK

实测 · 本机执行输出
  JDK 21  Random(42).nextInt(100) x5: 30 63 48 84 70
  JDK 21  Random(42).nextGaussian x3: 1.141905 0.919408 -0.949867
  JDK 21  SplittableRandom(42).nextInt(100) x5: 31 56 81 72 81
  JDK 21  L64X128MixRandom(42).nextInt(100) x5: 98 1 80 2 55
  JDK 21  Collections.shuffle(1..10, Random(42)): [5, 7, 3, 2, 8, 10, 9, 6, 4, 1]
  JDK 21  new Random(42).ints(5, 0, 100): [30, 63, 48, 84, 70]
  JDK 21  UUID.nameUUIDFromBytes("user-1"): d6d77053-92bc-3af6-b332-8bea8c4c6904
  JDK 17 与 JDK 21 的输出是否逐字相同: True
  JDK 27 与 JDK 21 的输出是否逐字相同: True

不能设种子的随机源

实测 · 本机执行输出
  setSeed 抛 UnsupportedOperationException;ThreadLocalRandom.nextInt(1000000) = 341563;UUID.randomUUID() = 640bc282…;SecureRandom.nextInt = 247235
  setSeed 抛 UnsupportedOperationException;ThreadLocalRandom.nextInt(1000000) = 472023;UUID.randomUUID() = 38602b71…;SecureRandom.nextInt = 118183
  setSeed 抛 UnsupportedOperationException;ThreadLocalRandom.nextInt(1000000) = 511739;UUID.randomUUID() = d5b1d863…;SecureRandom.nextInt = 501729
  setSeed 抛 UnsupportedOperationException;ThreadLocalRandom.nextInt(1000000) = 879280;UUID.randomUUID() = b1771607…;SecureRandom.nextInt = 512819
  setSeed 抛 UnsupportedOperationException;ThreadLocalRandom.nextInt(1000000) = 381557;UUID.randomUUID() = ba64d964…;SecureRandom.nextInt = 778828

多线程共享 Random

实测 · 本机执行输出
  共享 Random 的 6 次输出里,各线程的和各不相同的次数: 6 种;总和: ['3984002']
  示例(第 1 次):共享 Random(42):8 个线程各取 1000 个。各线程的和 [495525, 504221, 503055, 496996, 508801, 493644, 504543, 477217];总和 3984002
  示例(第 2 次):共享 Random(42):8 个线程各取 1000 个。各线程的和 [496717, 502138, 495755, 499636, 491628, 499255, 510003, 488870];总和 3984002
  split() 的 6 次输出共有 1 种;示例:按线程序号 split():各线程的和 [490087, 507120, 492009, 496279, 500532, 494600, 502308, 488481];总和 3971416

并行流生成数据

实测 · 本机执行输出
  并行流里共享 Random(42):前 8 个值 [123, 582, 244, 739, 275, 733, 843, 810],全体哈希 b2ec062;每个下标各自派生种子:前 8 个值 [31, 556, 481, 172, 781, 71, 856, 519],全体哈希 eabc6486
  并行流里共享 Random(42):前 8 个值 [645, 328, 708, 187, 282, 582, 12, 256],全体哈希 8d8bb398;每个下标各自派生种子:前 8 个值 [31, 556, 481, 172, 781, 71, 856, 519],全体哈希 eabc6486
  并行流里共享 Random(42):前 8 个值 [287, 994, 791, 960, 396, 818, 43, 539],全体哈希 e115c2da;每个下标各自派生种子:前 8 个值 [31, 556, 481, 172, 781, 71, 856, 519],全体哈希 eabc6486
  并行流里共享 Random(42):前 8 个值 [485, 391, 49, 31, 868, 216, 946, 30],全体哈希 e7450470;每个下标各自派生种子:前 8 个值 [31, 556, 481, 172, 781, 71, 856, 519],全体哈希 eabc6486
  并行流里共享 Random(42):前 8 个值 [555, 792, 203, 136, 215, 43, 507, 799],全体哈希 75880626;每个下标各自派生种子:前 8 个值 [31, 556, 481, 172, 781, 71, 856, 519],全体哈希 eabc6486
  共享 Random 的哈希共 5 种;各下标派生种子的哈希共 1 种

hashCode 作种子

实测 · 本机执行输出
  JDK 21 默认:"login".hashCode() = 103149417;Color.RED.hashCode() = 15db9742;new Object().hashCode() = 6d06d69c;Objects.hash(Color.RED, 1) = a59754c0;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 452b3a60
  JDK 21 默认:"login".hashCode() = 103149417;Color.RED.hashCode() = 15db9742;new Object().hashCode() = 6d06d69c;Objects.hash(Color.RED, 1) = a59754c0;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 452b3a60
  JDK 21 默认:"login".hashCode() = 103149417;Color.RED.hashCode() = 15db9742;new Object().hashCode() = 6d06d69c;Objects.hash(Color.RED, 1) = a59754c0;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 452b3a60
  JDK 17:"login".hashCode() = 103149417;Color.RED.hashCode() = 251a69d7;new Object().hashCode() = 7344699f;Objects.hash(Color.RED, 1) = 7e32d4cb;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 60e53bb2
  JDK 27:"login".hashCode() = 103149417;Color.RED.hashCode() = 1dbd16a6;new Object().hashCode() = 7ad041f3;Objects.hash(Color.RED, 1) = 99e5c1dc;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 4e0e2f49
  JDK 21 + -Duser.timezone=America/Los_Angeles:"login".hashCode() = 103149417;Color.RED.hashCode() = 15db9742;new Object().hashCode() = 6d06d69c;Objects.hash(Color.RED, 1) = a59754c0;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 452b3a60
  JDK 21 + -XX:+UseSerialGC:"login".hashCode() = 103149417;Color.RED.hashCode() = 15db9742;new Object().hashCode() = 6d06d69c;Objects.hash(Color.RED, 1) = a59754c0;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 452b3a60
  JDK 21 + -Xmx64m:"login".hashCode() = 103149417;Color.RED.hashCode() = 15db9742;new Object().hashCode() = 6d06d69c;Objects.hash(Color.RED, 1) = a59754c0;Key("a").hashCode() = 97;List.of(Color.GREEN).hashCode() = 452b3a60
  JDK 21 默认,代码里先对 3 个无关对象调用了 hashCode():先对 3 个无关对象调用 hashCode(),再取 Color.RED.hashCode() = 4e25154f

分步演示

测试数据生成
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 17.0.20、21.0.12、27;所有代码都是自写的最小程序,每个“重复 N 次”都是新的 JVM 进程
    • 没有验证:ThreadLocalRandom 与 SecureRandom 的内部算法、RandomGenerator 其他算法的跨版本稳定性(只测了 L64X128MixRandom)、Windows / Linux 上的相同种子输出(只在 macOS arm64 运行)、Instancio / jqwik 等库的种子机制、JUnit 对随机种子的报告
    京ICP备2026067502号-1京公网安备11010502063487号