首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 隨機數生成全方位指南:Math.random、Random、ThreadLocalRandom、SecureRandom 選型與陷阱

     編輯:Java 相關知識

Java 隨機數生成器選型決策樹與最佳實踐

從簡單的驗證碼到加密級密鑰,Java 提供四大隨機數 API。本文對比性能、線程安全、統計質量、安全性四個維度,給出場景化選型建議。

四大 API 對比矩陣

API底層算法線程安全統計質量加密安全典型場景
Math.random()Linear Congruential (LCG)是 (內部同步)低 (周期短、位相關)否簡單測試、教學演示
java.util.RandomLCG (48 位種子)是 (CAS 更新種子)中否一般業務、非安全隨機
ThreadLocalRandom (JDK 7+)LCG (64 位種子)極致 (ThreadLocal 隔離)中高否高併發、大量隨機數
SecureRandomOS 熵源 (SHA1PRNG, NativePRNG)是高 (經 NIST 測試)是密鑰、Token、Session ID、加密

核心用法速查

// 1. Math.random() - 最簡單但受限
double d = Math.random(); // [0.0, 1.0)
int n = (int) (Math.random() * 10); // [0, 9] 有偏差見下文

// 2. Random - 經典選擇
Random r = new Random();
int i = r.nextInt(10);        // [0, 9] 均勻
long l = r.nextLong();
boolean b = r.nextBoolean();
double g = r.nextGaussian();  // 高斯分佈 μ=0, σ=1

// 3. ThreadLocalRandom - 高併發首選 (JDK 7+)
int i = ThreadLocalRandom.current().nextInt(10);
long l = ThreadLocalRandom.current().nextLong(100, 200); // [100, 200) JDK 8+

// 4. SecureRandom - 安全級
SecureRandom sr = new SecureRandom();
byte[] key = new byte[32];
sr.nextBytes(key); // 256 位 AES 密鑰
String token = new BigInteger(130, sr).toString(32); // URL 安全隨機字符串

常見陷阱與修正

1. Math.random() 乘法偏差

// 錯誤:Math.round(Math.random() * 10) -> 0 和 10 概率是 1-9 的一半
// 正確:Math.floor(Math.random() * 11) -> [0, 10] 均勻
// 或直接用 Random/ThreadLocalRandom.nextInt(11)

2. Random 種子攻擊與絕對值陷阱

// 危險:Math.abs(r.nextInt()) % n -> 當 nextInt() == Integer.MIN_VALUE 時 abs 仍為負
// 正確:r.nextInt(n) 內部已處理拒絕採樣偏差

// 種子可預測:new Random() 使用 System.nanoTime(),啟動瞬間可被推測
// 安全場景必須用 SecureRandom

3. 多線程共享 Random 爭用

// 差:共享實例,CAS 重試開銷大
static final Random SHARED = new Random();

// 好:ThreadLocalRandom 完全無鎖
ThreadLocalRandom.current().nextInt();

// 或 ThreadLocal 包裝
static final ThreadLocal LOCAL = ThreadLocal.withInitial(Random::new);

進階場景解決方案

1. 指定範圍隨機整數 [min, max]

// JDK 8+ ThreadLocalRandom
ThreadLocalRandom.current().nextInt(min, max + 1); // 含上界

// 通用工具方法
public static int randomInt(int min, int max) {
    return ThreadLocalRandom.current().nextInt(min, max + 1);
}

2. 隨機字符串生成

// 字母數字 6 位驗證碼
String code = ThreadLocalRandom.current()
    .ints(6, 0, 36) // 0-35
    .mapToObj(i -> Character.toString(i < 10 ? '0' + i : 'A' + i - 10))
    .collect(Collectors.joining());

// URL 安全 Token (RFC 4648 Base64url)
SecureRandom sr = new SecureRandom();
byte[] bytes = new byte[24];
sr.nextBytes(bytes);
String token = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);

3. 加權隨機 / 概率分佈

// 簡單版:前綴和 + 二分查找
public static  T weightedRandom(List items, List weights) {
    double[] prefix = new double[weights.size()];
    prefix[0] = weights.get(0);
    for (int i = 1; i < weights.size(); i++) prefix[i] = prefix[i-1] + weights.get(i);
    double r = ThreadLocalRandom.current().nextDouble() * prefix[prefix.length-1];
    int idx = Arrays.binarySearch(prefix, r);
    return items.get(idx < 0 ? -idx - 1 : idx);
}

4. 可重現隨機 (測試/模擬)

// 固定種子
Random deterministic = new Random(12345L); // 每次運行序列相同
// 用於單元測試、壓力測試復現、蒙特卡洛模擬驗證

選型決策流程圖

需要加密安全?
  ├─ 是 → SecureRandom (唯一選擇)
  └─ 否 → 高併發 (多線程大量調用)?
          ├─ 是 → ThreadLocalRandom.current()
          └─ 否 → 簡單偶爾用?
                  ├─ 是 → Math.random() / new Random()
                  └─ 否 → 需可重現/特定分佈?
                          ├─ 是 → new Random(seed) + 自定義算法
                          └─ 否 → ThreadLocalRandom (通用最優)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved