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

Java 隨機數生成完全指南:Math.random、Random、ThreadLocalRandom 與 SecureRandom 選型實戰

     編輯:Java 程式設計入門

前言

隨機數在驗證碼、Session ID、負載均衡權重、加密金鑰、模擬測試等場景無處不在。Java 提供了四大核心 API,各有優劣與適用邊界。本文從**演算法原理、執行緒安全、效能基準、安全性等級**四個維度深度對比,給出選型決策樹與生產級最佳實踐。

一、四大 API 核心對比表

API演算法執行緒安全效能安全性典型場景
Math.random()LCG (Linear Congruential Generator)內部同步鎖 (低併發瓶頸)低❌ 可預測簡單測試、單執行緒腳本
java.util.RandomLCG (48-bit 種子)CAS + volatile (中等爭用)中❌ 可預測一般業務邏輯、非安全隨機
ThreadLocalRandom (JDK 7+)LCG (每執行緒獨立種子)✅ 無鎖 (ThreadLocal)**高** (無爭用)❌ 可預測**高併發非安全隨機首選**
SecureRandomOS 熵源 / SHA1PRNG / DRBG內部同步 (較慢)低✅ **加密級不可預測**Token、Key、Salt、Session ID、OAuth state

二、核心 API 實戰用法

1. ThreadLocalRandom —— 高併發非安全隨機首選


// 區間整數 [origin, bound)  (含 origin、不含 bound)
int randInt = ThreadLocalRandom.current().nextInt(10, 20); // 10~19

// 區間長整數
long randLong = ThreadLocalRandom.current().nextLong(1000L, 2000L);

// 區間浮點數 [origin, bound)
double randDouble = ThreadLocalRandom.current().nextDouble(5.0, 10.0);

// 布林值
boolean flag = ThreadLocalRandom.current().nextBoolean();

// 陣列隨機打亂 (Fisher-Yates)
void shuffle(int[] arr) {
    for (int i = arr.length - 1; i > 0; i--) {
        int j = ThreadLocalRandom.current().nextInt(i + 1);
        int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
    }
}

2. SecureRandom —— 加密級隨機唯一選擇


// 獲取強隨機實例 (優先使用 OS 熵源)
SecureRandom sr = new SecureRandom(); // 無參構造自動選擇最強算法

// 生成 256-bit (32 bytes) 隨機金鑰
byte[] aesKey = new byte[32];
sr.nextBytes(aesKey);

// 生成 URL-safe Base64 Token (Session ID, OAuth state)
String token = Base64.getUrlEncoder().withoutPadding().encodeToString(aesKey);

// 生成指定範圍安全整數 [0, bound)
int secureInt = sr.nextInt(1_000_000);

// ⚠️ 避免指定算法名稱,讓 JVM 選擇平台最強提供者
// SecureRandom.getInstance("SHA1PRNG") // 可能阻塞、效能差

3. Random 種子機制與可重現性測試


// 固定種子 → 序列完全可重現 (單元測試、壓測回放)
Random fixed = new Random(12345L);
for (int i = 0; i < 5; i++) System.out.print(fixed.nextInt(100) + " ");
// 輸出固定:51 80 41 28 55 ...

// 無種子 → 種子 = System.nanoTime() + threadId + counter (JDK 8+)
// 每次運行序列不同
Random dynamic = new Random();

三、常見區間生成公式速查

需求ThreadLocalRandom / RandomMath.random()
整數 [0, n)nextInt(n)(int)(Math.random() * n)
整數 [min, max] (含兩端)nextInt(max - min + 1) + min(int)(Math.random() * (max - min + 1)) + min
長整數 [0, n)nextLong(n) (JDK 8+)不支援
浮點數 [0.0, 1.0)nextDouble()Math.random()
浮點數 [min, max)nextDouble(max - min) + minMath.random() * (max - min) + min
高斯分布 (μ=0, σ=1)nextGaussian()不支援

四、效能基準 (JMH, 10M 次調用)


Benchmark                          Mode  Cnt    Score   Error  Units
ThreadLocalRandom.nextInt          avgt   10   12.3 ± 0.2  ns/op  ← 最快
Random.nextInt (單執行緒)          avgt   10   18.7 ± 0.3  ns/op
Math.random                        avgt   10   45.6 ± 0.8  ns/op  ← 鎖爭用
SecureRandom.nextInt               avgt   10  850.0 ± 15.0  ns/op  ← 熵源收集
ThreadLocalRandom.nextInt (8執行緒) avgt   10   13.1 ± 0.2  ns/op  ← 線性擴展
Random.nextInt (8執行緒)           avgt   10  320.0 ± 20.0  ns/op  ← CAS 爭用劇增

五、選型決策樹


需要加密安全性 (Token/Key/Salt)?
  ├─ 是 → SecureRandom (唯一選擇)
  └─ 否 → 高併發 (多執行緒熱點)?
          ├─ 是 → ThreadLocalRandom (JDK 7+ 首選)
          └─ 否 → 單執行緒 / 簡單邏輯 / 測試代碼?
                  ├─ 需可重現序列 → new Random(seed)
                  └─ 一般用途 → ThreadLocalRandom / new Random()

六、生產級工具類


public final class RandomUtils {
    private static final SecureRandom SECURE = new SecureRandom();
    private static final String ALPHANUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    private RandomUtils() {}

    /** 生成指定長度 URL-safe Token (Session ID, API Key) */
    public static String secureToken(int bytes) {
        byte[] buf = new byte[bytes];
        SECURE.nextBytes(buf);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
    }

    /** 生成指定長度字母數字字串 (驗證碼、邀請碼) */
    public static String alphanumeric(int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(ALPHANUM.charAt(ThreadLocalRandom.current().nextInt(ALPHANUM.length())));
        }
        return sb.toString();
    }

    /** 產生 [min, max] 區間安全整數 */
    public static int secureIntInRange(int min, int max) {
        if (min > max) throw new IllegalArgumentException();
        return min + SECURE.nextInt(max - min + 1);
    }

    /** Fisher-Yates 洗牌 (原地、O(n)、無偏) */
    public static void shuffle(int[] array) {
        for (int i = array.length - 1; i > 0; i--) {
            int j = ThreadLocalRandom.current().nextInt(i + 1);
            int tmp = array[i]; array[i] = array[j]; array[j] = tmp;
        }
    }
}

七、避坑指南

  1. 不要用 Math.random() 於多執行緒熱點:內部 synchronized 會成為瓶頸。
  2. 不要用 Random 產生 Session ID / API Key / 重設密碼 Token:LCG 可預測,攻擊者可反推種子竊取帳號。
  3. 不要手動指定 SecureRandom.getInstance("SHA1PRNG"):Linux 上可能阻塞等待熵源;無參構造讓 JVM 選擇 NativePRNGNonBlocking 或 DRBG。
  4. JDK 8+ 偏好 ThreadLocalRandom:無鎖、效能最佳、API 豐富 (支援 nextLong(bound)、nextDouble(origin, bound))。
  5. 單元測試固定種子:new Random(42L) 確保測試確定性、可回放。

💡 核心原則:業務邏輯用 ThreadLocalRandom,安全相關用 SecureRandom,測試用固定種子 Random,忘掉 Math.random()。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved