隨機數是程式設計基礎設施,但正確、高效、線程安全地生成隨機數卻暗藏細節。本文系統梳理 Java 隨機數 API 演進脈絡,從 Math.random() 到 ThreadLocalRandom,揭示常見誤區與生產級選型指南。
| 版本 | API | 特點 | 適用場景 |
|---|---|---|---|
| JDK 1.0 | Math.random() | 靜態方法、返回 double、內部共享 Random 實例 | 簡單單線程、低頻率 |
| JDK 1.0 | java.util.Random | 實例方法、支援多型別、線程安全(CAS)、種子可控 | 通用、需可重現序列 |
| JDK 1.4 | java.security.SecureRandom | 密碼學強度、熵源、效能較低 | Token、Session ID、加密金鑰 |
| JDK 7 | ThreadLocalRandom | 線程本地、無競爭、current() 靜態存取、效能最高 | 高併發、非安全敏感 |
| JDK 8 | SplittableRandom | 可分割、適合 Fork/Join、並行流 | 並行計算、Stream API |
// 錯誤:分佈不均 int n = (int) Math.round(Math.random() * 10); // 0 和 10 機率只有其他數字一半 // 正確:使用 Random.nextInt(bound) int n = new Random().nextInt(11); // [0, 10] 均勻分佈
// 低效:大量物件分配、種子碰撞風險
for (int i = 0; i < 10000; i++) {
int n = new Random().nextInt(100);
}
// 正確:共享實例或 ThreadLocalRandom
static final Random SHARED = new Random(); // 線程安全但有競爭
// 或
int n = ThreadLocalRandom.current().nextInt(100); // 無競爭、最快
// 危險:nextInt() 可能返回 Integer.MIN_VALUE,Math.abs 仍為負數 // 模運算進一步放大分佈偏差 // 正確:直接使用 nextInt(bound) int n = rnd.nextInt(bound); // 內部處理邊界、分佈均勻
| 場景 | 推薦 | 理由 |
|---|---|---|
| 高併發非安全業務 | ThreadLocalRandom.current() | 零競爭、JIT 優化友好、API 豐富 |
| 需可重現序列(測試/模擬) | 共享 Random + 固定種子 | 確定性、可回放 |
| 安全敏感 | SecureRandom | 密碼學強度、不可預測 |
| 並行流/ ForkJoin | SplittableRandom | 可分割、負載均衡 |
| 線程隔離需求 | ThreadLocal<Random> | 完全隔離、自定義種子 |
// 核心用法 int n = ThreadLocalRandom.current().nextInt(100); long l = ThreadLocalRandom.current().nextLong(1000L); double d = ThreadLocalRandom.current().nextDouble(); boolean b = ThreadLocalRandom.current().nextBoolean(); // 範圍指定 int inRange = ThreadLocalRandom.current().nextInt(10, 20); // [10, 20) // 內部原理:Thread 類包含 threadLocalRandomSeed、threadLocalRandomProbe、threadLocalRandomSecondarySeed // 每線程獨立狀態,無 CAS 競爭,JIT 可內聯優化
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class RandomBenchmark {
static final Random SHARED = new Random();
@Benchmark public int sharedRandom() { return SHARED.nextInt(100); }
@Benchmark public int threadLocalRandom() { return ThreadLocalRandom.current().nextInt(100); }
@Benchmark public int newRandomEach() { return new Random().nextInt(100); }
}
典型結果:ThreadLocalRandom ≫ Shared Random ≫ New Random Each(差距可達 10-100 倍)。
需要密碼學安全?
├─ 是 → SecureRandom
└─ 否 → 高併發非安全?
├─ 是 → ThreadLocalRandom.current()
└─ 否 → 需可重現序列?
├─ 是 → new Random(fixedSeed) 共享實例
└─ 否 → 單線程/低頻 → Math.random() 或共享 Random
隨機數生成不再是「隨便呼叫」。理解 API 演進邏輯、分佈特性、並發行為,才能寫出正確且高效的代碼。