java基础

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:

  1. 开源中国:Java中的random函数是如何实现的
  2. System.nanoTime与System.currentTimeMillis的区别
上一篇下一篇

猜你喜欢

热点阅读