java中Random實現原理
阿新 • • 發佈:2021-07-06
Random使用
java中使用Random類來產生隨機數。
import java.util.Random; public class Client { public static void main(String[] args) { test(); test(); } private static void test() { Random random = new Random(10000); for (int i = 0; i < 5; i++) { System.out.print(random.nextInt(10000) + " "); } System.out.println(); } }
輸出結果為
2208 572 9116 3475 4500
2208 572 9116 3475 4500
只要種子相同,產生的隨機數序列就是相同的,所以說Random是一種偽隨機數的實現。
Random原理
/** * 隨機數種子 */ private final AtomicLong seed; /** * 無參構造器,使用當前時間納秒值 */ public Random() { this(seedUniquifier() ^ System.nanoTime()); } private static long seedUniquifier() { // L'Ecuyer, "Tables of Linear Congruential Generators of // Different Sizes and Good Lattice Structure", 1999 for (;;) { long current = seedUniquifier.get(); long next = current * 1181783497276652981L; if (seedUniquifier.compareAndSet(current, next)) return next; } }
無參構造器使用當前時間當做建立種子的一部分,可以看做每次都是不同的。
/** * */ 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)); return (int)(nextseed >>> (48 - bits)); }
通過一個固定演算法,使用CAS將一箇舊的種子更新為新種子。
ThreadLocalRandom
Random獲取隨機數使用CAS更新種子,在高併發環境下會大量自旋重試,效能下降,這種情況下可以使用ThreadLocalRandom。
import java.util.concurrent.ThreadLocalRandom;
public class Client2 {
public static void main(String[] args) {
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
for (int i = 0; i < 5; i++) {
System.out.print(threadLocalRandom.nextInt(100) + " ");
}
}
}
輸出為
73 78 0 68 12
通過一個靜態方法建立,不能自己設定種子。
public class ThreadLocalRandom extends Random {
// 可以直接操作記憶體的工具類
private static final Unsafe U = Unsafe.getUnsafe();
// 種子
private static final long SEED = U.objectFieldOffset
(Thread.class, "threadLocalRandomSeed");
private static final long PROBE = U.objectFieldOffset
(Thread.class, "threadLocalRandomProbe");
/** 單例 */
static final ThreadLocalRandom instance = new ThreadLocalRandom();
/**
* 獲取單例物件
*/
public static ThreadLocalRandom current() {
if (U.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
}
使用餓漢式的單例模式來建立ThreadLocalRandom物件
/**
* Returns a pseudorandom {@code int} value.
*
* @return a pseudorandom {@code int} value
*/
public int nextInt() {
return mix32(nextSeed());
}
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
U.putLong(t = Thread.currentThread(), SEED,
r = U.getLong(t, SEED) + GAMMA);
return r;
}
ThreadLocalRandom每個執行緒儲存一份種子,每次更新自己執行緒的種子,避免高併發下的競爭。
/** The current seed for a ThreadLocalRandom */
@jdk.internal.vm.annotation.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomProbe;
種子儲存在Thread類下threadLocalRandomSeed和threadLocalRandomProbe欄位,原理類似於ThreadLocal,用空間換時間。
SecureRandom
import java.security.SecureRandom;
public class Client3 {
public static void main(String[] args) {
SecureRandom secureRandom = new SecureRandom();
System.out.println(secureRandom.getAlgorithm());
for (int i = 0; i < 5; i++) {
System.out.print(secureRandom.nextInt(100) + " ");
}
}
}
輸出為
DRBG
7 23 20 92 31
SecureRandom是一個強隨機數生成器,會收集計算機的各種資訊,使用加密演算法建立隨機數。windows下預設使用DRBG演算法。