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

Java 隨機數完全指南:Math.random() vs Random 類 vs ThreadLocalRandom vs SecureRandom

     編輯:Java 相關知識

Java 提供多層次隨機數 API,從簡單的 `Math.random()` 到密碼學級的 `SecureRandom`。選擇錯誤會導致**效能瓶頸、線程爭用、安全漏洞、統計偏差**。本文系統對比四大核心 API,給出生產環境選型指南。

四大 API 快速對比

API類別執行緒安全演算法速度安全性適用場景
`Math.random()`靜態方法內部同步 (鎖爭用)LinearCongruentialGenerator (LCG)慢 (同步開銷)低 (可預測)簡單測試、單執行緒、非關鍵
`java.util.Random`類同步方法 (鎖爭用)LCG (48 位種子)中低 (可預測)一般模擬、單執行緒、非安全
`ThreadLocalRandom`類 (JDK 7+)**無鎖、執行緒本地**LCG (64 位種子)**快 (無同步)**低**高併發、非安全、JDK 7+ 首選**
`SecureRandom`類同步OS 熵源 / DRBG (SHA1PRNG 等)慢 (熵收集)**高 (密碼學級)****Token、Key、Session ID、加密**

核心 API 詳細用法

1. Math.random() - 最簡單但效能最差


// 返回 [0.0, 1.0) double
double d = Math.random(); 

// 常見轉換陷阱:生成 [min, max] 整數
// 錯誤:(int)(Math.random() * max) + min  // 分佈不均
// 正確:[min, max] 閉區間
int randomInt = (int)(Math.random() * (max - min + 1)) + min;
// 正確:[min, max) 半開區間
int randomInt = (int)(Math.random() * (max - min)) + min;

缺陷: 內部單例 `Random` 共享,多執行緒嚴重鎖爭用,JDK 8+ 已不推薦高併發使用。

2. java.util.Random - 經典可控種子


Random r = new Random();           // 系統時間種子
Random r2 = new Random(12345L);    // 固定種子 → 確定性序列 (測試/重現)

// 常用方法
boolean b = r.nextBoolean();           // 50% true
double d = r.nextDouble();             // [0.0, 1.0)
float f = r.nextFloat();               // [0.0, 1.0f)
int i = r.nextInt();                   // [-2^31, 2^31-1]
int bounded = r.nextInt(100);          // [0, 100) 均勻分佈
long l = r.nextLong();                 // 全長整數範圍
byte[] bytes = new byte[16]; r.nextBytes(bytes); // 填充位元組陣列

// 指定區間 [min, max]
int inRange = r.nextInt(max - min + 1) + min; // 閉區間
// 避免:r.nextInt() % n + min (負數餘數導致分佈偏移)

種子可重現性: 相同種子 `new Random(seed)` 產生**完全相同序列**,利於單元測試、模擬重現、除錯。

3. ThreadLocalRandom - 高併發王者 (JDK 7+)


// 直接使用靜態方法,無需實例化
int i = ThreadLocalRandom.current().nextInt(100); // [0, 100)
long l = ThreadLocalRandom.current().nextLong(1000, 2000); // [1000, 2000) JDK 8+
double d = ThreadLocalRandom.current().nextDouble(0.5, 1.5); // [0.5, 1.5) JDK 8+

// 併發效能提升 10-100x (無鎖、無緩存行爭用)
// 內部每執行緒獨立種子,自動初始化 (混合系統熵、執行緒 ID、時間)

原理: `ThreadLocal` 每執行緒獨立實例,CAS 更新種子,零同步開銷。**並行流、ForkJoinPool、高併發業務首選。**

4. SecureRandom - 安全關鍵場景唯一選擇


// 獲取預設強隨機實例 (通常採用 OS 熵源: /dev/urandom, Windows CryptoAPI)
SecureRandom sr = new SecureRandom();

// 生成安全 Token (32 位元組 = 256 位熵)
byte[] token = new byte[32];
sr.nextBytes(token);
String secureToken = Base64.getUrlEncoder().withoutPadding().encodeToString(token);

// 生成 AES-256 金鑰
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, sr);
SecretKey key = kg.generateKey();

// 種子管理:生產環境**不要**手動 setSeed!依賴 OS 熵源自動播種
// sr.setSeed(...) 會降低熵品質,僅用於測試重現

效能提示: `SecureRandom` 首次調用可能阻塞等待熵池 (Linux `/dev/random` 行為),建議啟動期預熱或使用 `-Djava.security.egd=file:/dev/./urandom` (非阻塞)。

區間隨機數統一工具類 (生產就緒)


public final class RandomUtils {
    private RandomUtils() {}
    
    // 非安全、高併發首選
    public static int nextInt(int minInclusive, int maxInclusive) {
        return ThreadLocalRandom.current().nextInt(minInclusive, maxInclusive + 1);
    }
    public static long nextLong(long minInclusive, long maxInclusive) {
        return ThreadLocalRandom.current().nextLong(minInclusive, maxInclusive + 1);
    }
    public static double nextDouble(double minInclusive, double maxExclusive) {
        return ThreadLocalRandom.current().nextDouble(minInclusive, maxExclusive);
    }
    
    // 安全級 (Token、Key、Session)
    public static String secureToken(int byteLength) {
        byte[] bytes = new byte[byteLength];
        SecureRandomHolder.INSTANCE.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }
    public static byte[] secureBytes(int length) {
        byte[] bytes = new byte[length];
        SecureRandomHolder.INSTANCE.nextBytes(bytes);
        return bytes;
    }
    
    // 延遲初始化 SecureRandom 避免啟動阻塞
    private enum SecureRandomHolder {
        INSTANCE;
        private final SecureRandom sr;
        SecureRandomHolder() {
            // 可指定演算法: "NativePRNGNonBlocking" (Linux), "Windows-PRNG"
            this.sr = new SecureRandom(); 
        }
    }
}

選型決策樹

需要密碼學安全嗎?
├─ 是 → SecureRandom (Token、Key、Session ID、Nonce、加密隨機數)
└─ 否 → 高併發環境 (Web 容器、並行流、多執行緒)?
        ├─ 是 → ThreadLocalRandom (JDK 7+ 標準、零鎖、高吞吐)
        └─ 否 → 單執行緒/測試/需固定種子重現?
                ├─ 需固定種子 → new Random(seed)
                └─ 簡單臨時用 → Math.random() (不推薦生產)

常見錯誤與避坑

錯誤代碼問題修正
`new Random().nextInt(10)` 在循環中頻繁創建對象、種子可能重複 (系統時間粒度)複用實例或用 `ThreadLocalRandom`
`Math.abs(r.nextInt() % n)``Integer.MIN_VALUE` 絕對值仍為負、分佈偏移`r.nextInt(n)` 或 `ThreadLocalRandom.current().nextInt(n)`
`SecureRandom` 用於大量非安全隨機極慢 (熵收集、同步)改用 `ThreadLocalRandom`
`sr.setSeed(System.currentTimeMillis())`大幅降低熵品質、可預測**永不**手動播種 SecureRandom (除非測試)
並行流中用共享 `Random`嚴重鎖爭用、效能崩潰`ThreadLocalRandom` 或 `SplittableRandom`

進階:SplittableRandom (JDK 8+ 並行流專用)


// 可分割的隨機產生器,專為並行流設計
SplittableRandom sr = new SplittableRandom();
IntStream ints = sr.ints(1_000_000, 0, 100); // 產生 100 萬 [0,100) 整數流
// 可分割:sr.split() 產生獨立子產生器,避免競爭

總結:**非安全高併發用 `ThreadLocalRandom`,安全關鍵用 `SecureRandom`,測試重現用 `new Random(seed)`,拋棄 `Math.random()` 和共享 `Random` 實例。** 正確選型能同時提升效能、安全性與代碼可維護性。

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