Java Random源码分析
2017-04-13 本文已影响424人
一个理想主义的大兵
一、Random类的构造函数,官方文档如下:
Random类的构造函数
1.无参的构造函数,源码如下:
/**
* Creates a new random number generator. This constructor sets
* the seed of the random number generator to a value very likely
* to be distinct from any other invocation of this constructor.
*/
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
其中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 * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
/**
* Returns the current value of the running Java Virtual Machine's
* high-resolution time source, in nanoseconds.
*
* <p>This method can only be used to measure elapsed time and is
* not related to any other notion of system or wall-clock time.
* The value returned represents nanoseconds since some fixed but
* arbitrary <i>origin</i> time (perhaps in the future, so values
* may be negative). The same origin is used by all invocations of
* this method in an instance of a Java virtual machine; other
* virtual machine instances are likely to use a different origin.
*
* <p>This method provides nanosecond precision, but not necessarily
* nanosecond resolution (that is, how frequently the value changes)
* - no guarantees are made except that the resolution is at least as
* good as that of {@link #currentTimeMillis()}.
*
* <p>Differences in successive calls that span greater than
* approximately 292 years (2<sup>63</sup> nanoseconds) will not
* correctly compute elapsed time due to numerical overflow.
*
* <p>The values returned by this method become meaningful only when
* the difference between two such values, obtained within the same
* instance of a Java virtual machine, is computed.
*
* <p> For example, to measure how long some code takes to execute:
* <pre> {@code
* long startTime = System.nanoTime();
* // ... the code being measured ...
* long estimatedTime = System.nanoTime() - startTime;}</pre>
*
* <p>To compare two nanoTime values
* <pre> {@code
* long t0 = System.nanoTime();
* ...
* long t1 = System.nanoTime();}</pre>
*
* one should use {@code t1 - t0 < 0}, not {@code t1 < t0},
* because of the possibility of numerical overflow.
*
* @return the current value of the running Java Virtual Machine's
* high-resolution time source, in nanoseconds
* @since 1.5
*/
public static native long nanoTime();
「注:其中nanoTime()
函数,用native关键字修饰,由本地代码实现。」
两者异或运算后,初始化类成员变量seed
,此变量会成为随机函数的种子,所有的随机算法都由种子数算起,定义如下:
/**
* The internal state associated with this pseudorandom number generator.
* (The specs for the methods in this class describe the ongoing
* computation of this value.)
*/
private final AtomicLong seed;
2.有参的构造函数,源码如下:
/**
* Creates a new random number generator using a single {@code long} seed.
* The seed is the initial value of the internal state of the pseudorandom
* number generator which is maintained by method {@link #next}.
*
* <p>The invocation {@code new Random(seed)} is equivalent to:
* <pre> {@code
* Random rnd = new Random();
* rnd.setSeed(seed);}</pre>
*
* @param seed the initial seed
* @see #setSeed(long)
*/
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);
}
}
构造函数由传入的种子参数进行初始化,不难理解,值得注意的是,由同一个种子初始化的Random对象,相同次数调用时,产生的随机数相同。举例如下:
public static void main(String[] args) {
Random random = new Random(47);
for(int i = 0; i < 3; i++){
System.out.print(random.nextInt(20) + " ");
}
System.out.println();
Random randomII = new Random(47);
for(int i = 0; i < 3; i++){
System.out.print(randomII.nextInt(20)+ " ");
}
}
输出如下:
18 15 13
18 15 13
二、Java随机数原理
大多数语言实现伪随机数,均采用线性同余方程(linear congruential generator),详细内容参照「资料1」.
Java中使用的是48-bit的种子,然后调用线性同余方程,源码如下:
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));
}
经常在随机数中调用的函数nextInt()
,源码如下:
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = next(31);
int m = bound - 1;
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;
}
此处实现应该是最新版,与资料中提供的源码略有不同,函数中几处细节尚未理解,想清楚之后再回来补充。
Reference: