| 方式 | API | 特點 | 適用場景 |
|---|---|---|---|
| Math.random() | static double | 0.0~1.0、內部用 Random、線程安全但效能較差 | 簡單臨時需求 |
| java.util.Random | 實例方法 | 支援 int/long/float/double/boolean、可設種子、非線程安全 | 一般業務邏輯、模擬測試 |
| System.currentTimeMillis() | long 時間戳 | 毫秒精度、可作種子、非真正隨機 | 簡易種子來源 |
| ThreadLocalRandom | JDK 7+ 實例 | 線程隔離、無競爭、效能最佳 | 高併發環境 (推薦) |
| SecureRandom | CSPRNG | 密碼學安全、熵池收集、效能較低 | Token/Key/Session ID 生成 |
// JDK 8 Random 核心實作
private static final long multiplier = 0x5DEECE66DL; // 25214903917
private static final long addend = 0xBL; // 11
private static final long mask = (1L << 48) - 1; // 48 位元掩碼
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed)); // CAS 無鎖更新
return (int)(nextseed >>> (48 - bits));
}
數學公式:X_{n+1} = (a * X_n + c) mod m
public Random() {
this(seedUniquifier() ^ System.nanoTime()); // 組合唯一化器 + 奈秒時間
}
private static long seedUniquifier() {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
return seedUniquifier.compareAndSet(current, next) ? next : seedUniquifier();
}
public Random(long seed) {
this.seed = new AtomicLong(initialScramble(seed));
}
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask; // 混淆種子避免弱種子
}
public class SeedDemo {
public static void main(String[] args) {
// 相同種子 → 相同序列 (可重現性)
Random r1 = new Random(12345);
Random r2 = new Random(12345);
System.out.println(r1.nextInt() == r2.nextInt()); // true
System.out.println(r1.nextInt() == r2.nextInt()); // true
// 不同種子 → 不同序列
Random r3 = new Random(12345);
Random r4 = new Random(54321);
System.out.println(r3.nextInt() == r4.nextInt()); // false (極大概率)
}
}
// glibc 簡化版
static unsigned long int next = 1;
int rand(void) {
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
void srand(unsigned int seed) { next = seed; }
// 經典 DOS 時代從 BIOS 計時器取種子 (0x0040:0x006C)
void random_start(void) {
unsigned short* timer = (unsigned short*)0x0040006C; // 記憶體映射位址
RAND_SEED = *timer; // 低 16 位
// 現代 OS 提供 /dev/urandom, CryptGenRandom, getrandom() 等熵源
}
結論:計算機無法產生真隨機,所有「隨機」皆為確定性演算法 + 熵源種子。
相同種子在不同 JDK 版本/作業系統可能產生不同序列!
| 演算法 | 提供者 | 特點 |
|---|---|---|
| SHA1PRNG | SUN | SHA-1 基礎、傳統預設 |
| NativePRNG | SunMSCAPI | Windows CryptoAPI |
| NativePRNGBlocking | SunPKCS11 | /dev/random (阻塞) |
| NativePRNGNonBlocking | SunPKCS11 | /dev/urandom (非阻塞) |
| DRBG | SunJCE | NIST SP 800-90A 標準 (JDK 9+) |
// 1. 明確指定演算法與提供者 (避免預設差異)
SecureRandom sr = SecureRandom.getInstanceStrong(); // JDK 8+ 推薦
// 2. 生成安全 Token (URL-safe Base64)
public String generateSecureToken(int byteLength) {
byte[] bytes = new byte[byteLength];
sr.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
// 3. 避免重複建立實例 (昂貴操作)
// 單例或注入共用實例
Benchmark Mode Cnt Score Error Units Random.nextInt thrpt 10 45.2 ± 0.3 M ops/s ThreadLocalRandom.nextInt thrpt 10 120.5 ± 1.2 M ops/s (2.6x) SecureRandom.nextInt thrpt 10 0.8 ± 0.1 M ops/s (56x slower) SplittableRandom.nextInt thrpt 10 95.3 ± 0.8 M ops/s (ForkJoin 友善)
ThreadLocalRandom.current().nextInt() (JDK 7+)SplittableRandom (JDK 8+)new Random(fixedSeed)SecureRandom.getInstanceStrong()Math.random() (同步瓶頸)、new Random() 在高併發 (種子競爭)