利用多線程調度的非確定性(系統負載、內存狀態、CPU 時間片等不可控因素)作為熵源,通過13 輪線程競態放大微小差異(胡蝶效應),最終產生 [0,1) 區間的偽隨機數。
import java.util.concurrent.atomic.AtomicInteger;
public class CustomRandomGenerator implements Runnable {
// 共享狀態(靜態變量跨線程可見)
private static final int M = 1 << 16; // 65536
private static final int[] R = new int[M];
private static final AtomicInteger f = new AtomicInteger(127);
private static int randomValue;
private static int x = 13;
static {
// 初始化 R 陣列:線性同餘生成器 (LCG) 填充 0~65535
R[0] = 13849;
for (int i = 1; i < M; i++) {
R[i] = (2053 * R[i - 1] + 13849) % M;
}
// 打亂最後兩個元素增加混淆
int k = R[M - 1];
R[M - 1] = R[(f.get() + 1) % M];
R[(f.get() + 1) % M] = k;
}
@Override
public void run() {
// 緊循環更新共享狀態,直到被中斷
while (!Thread.currentThread().isInterrupted()) {
int currentF = f.get();
int nextF = ((currentF / 2) + R[currentF]) % M;
f.set(nextF);
randomValue = R[nextF];
}
}
/** 核心方法:獲取 [0,1) 隨機數 */
public static double nextDouble() {
double[] samples = new double[13];
for (int i = 0; i < 13; i++) {
Thread t = new Thread(new CustomRandomGenerator());
t.start();
try {
Thread.sleep(x + 1); // 讓線程運行極短時間
} catch (InterruptedException ignored) {}
t.interrupt(); // 停止線程
// 采樣當前隨機值
double sample = (double) randomValue / M;
samples[i] = sample;
// 動態調整參數增加熵
x = f.get() % 13;
shuffleR(11 + f.get() / 7);
// 避免不動點(連續采樣相同)
if (i > 0 && samples[i] == samples[i - 1]) {
shuffleR(13 + f.get() / 11);
}
}
return samples[12]; // 返回第 13 次采樣
}
/** 循環移位打亂 R 陣列 */
private static void shuffleR(int shift) {
int[] copy = R.clone();
for (int i = 0; i < M; i++) {
R[i] = copy[(i + shift) % M];
}
}
public static void main(String[] args) {
System.out.println("隨機數: " + nextDouble());
}
}
| 機制 | 作用 |
|---|---|
| LCG 初始化 R 陣列 | 生成確定性但看似隨機的排列,作為基礎查找表 |
| 多線程緊循環更新 f | 利用線程調度不確定性(上下文切換時機、緩存未命中等)引入熵 |
| 13 輪采樣 + 參數動態調整 | 迭代放大微小差異(胡蝶效應),x、shuffleR 參數依賴前一輪結果 |
| 不動點檢測與打破 | 連續兩次采樣相同說明進入循環,強制打亂 R 陣列跳出 |
| AtomicInteger 保證可見性 | 多線程間共享狀態無鎖同步 |
public class RandomQualityTest {
public static void main(String[] args) {
int N = 1_000_000;
long sum = 0;
long start = System.nanoTime();
for (int i = 0; i < N; i++) {
sum += CustomRandomGenerator.nextDouble() * 1_000_000;
}
long elapsed = System.nanoTime() - start;
System.out.printf("生成 %d 個耗時: %.2f ms, 平均: %.2f%n",
N, elapsed / 1e6, (double) sum / N / 1_000_000);
}
}
典型輸出:生成 100 萬個約需 15-30 秒(極慢),平均值約 0.5。
結論:統計分布均勻,但性能極差(每次調用創建 13 個線程、睡眠、中斷)。
Math.random() ~1ns,慢 1000 萬倍。| 場景 | 推薦類 | 特點 |
|---|---|---|
| 一般用途 | java.util.Random | 線程安全、種子可控、速度快 |
| 多線程高併發 | ThreadLocalRandom (JDK 7+) | 無競爭、極快 |
| 加密/安全 | java.security.SecureRandom | 密碼學強度、熵源來自 OS |
| 科學計算/大規模 | SplittableRandom (JDK 8+) | 可分割、並行流友好 |