// 默认 seedUniquifier 进行累乘计算,依赖的是系统的 CAS 操作 // 当多线程的时候,这里可能会出现耗时等待,性能损失,所以在多线程的情况下,推荐 ThreadLocalRandom 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 * 181783497276652981L; if (seedUniquifier.compareAndSet(current, next)) return next; } }
// 如果有指定seed,那么将seed 进行初始化,转化成48位的seed值 public Random(long seed) { if (getClass() == Random.class) this.seed = new AtomicLong(initialScramble(seed)); else { // subclass might have overriden setSeed this.seed = new AtomicLong(); setSeed(seed); } }
因为 int 的字节长度位32位,所以在生成的随机数(48位)需要进行移位操作到32位空间,但移位的同时要注意的一个点,当使用者指定取值区间 bound的时候,因为 next() 生成随机数的区间较大,当取值区间变小后,移位的过程中,会分为多个取值空间,导致不同空间的随机数概率不同。
当 n 是2的整数次幂时,n 肯定会被 $2^31$ 整除,这时候可以直接映射,只需运算一次 next(31);
当 n 不是2的整数次幂,那么就会出现刚才那样分配不均匀的情况,通过 u - (r = u % bound) + m < 0 判断是否分布均匀, u - (r = u % bound) 表示临界点的值,由于 m = bound - 1,如果这时临界值 + m < 1,那么说明发生了溢出,那么就可以通过判断是否溢出来判断生成的随机数是否在最后一个区间,如果是的话再进行一次 next(31) 重试
public int nextInt(int bound) { if (bound <= 0) throw new IllegalArgumentException(BadBound);
// 获取31位的随机数 int r = next(31); int m = bound - 1;
// 判断取值区间 bound,是不是为2的整数次幂 if ((bound & m) == 0) // i.e., bound is a power of 2 r = (int)((bound * (long)r) >> 31); else { for (int u = r; u - (r = u % bound) + m < 0; u = next(31)); } return r; }