Java 获取随机数

424 阅读1分钟

Math.random() 方法

调用方法
public static void main(String[] args) {
		 System.out.println(Math.random()); //0-1 的随机数
	}

源码如下:

public static double random() {

   return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
   }

randomNumberGenerator 的生成源码

private static final class RandomNumberGeneratorHolder {
   static final Random randomNumberGenerator = new Random();
}

所以从源码上看,Math.random() = Random.nextDouble(),这是线程安全的,因为底层实现CAS(Compare and Swap)生成随机数,Java 并发机制实现原子操作有两种:一种是锁,一种是 CAS。

Random()方法

调用方法
	public static void main(String[] args) {
		
		Random random1=new Random(2);
	    random1.nextInt(100);
	
	}

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 * 181783497276652981L;
            if (seedUniquifier.compareAndSet(current, next)) // CAS(Compare and Swap)生成随机数
                return next;
        }
    }

有参的构造方法

  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);
        }
    }
  1. 发现当Random()空参时,构造方法里使用当前系统时间相关的一个数字作为种子数,该种子数只作为随机算法的起源数字,与生成的随机数区间无关系。这个是线程安全的,因为底层实现CAS(Compare and Swap)生成随机数。

2.如果 Random 的随机种子一样的话,每次生成的随机数都是可预测的(都是一样的)。 代码如下

Random random1=new Random(2);
		Random random2=new Random(2);
		
		for(int index=0;index<10;index++)
		{
			System.out.println("random1--"+random1.nextInt(100));
			
			System.out.println("random2--"+random2.nextInt(100));
		}
		
		System.out.println(Math.random());

输出如下

random1--8
random2--8
random1--72
random2--72
random1--40
random2--40
random1--67
random2--67
random1--89
random2--89
random1--50
random2--50
random1--6
random2--6
random1--19
random2--19
random1--47
random2--47
random1--68
random2--68

ThreadLocalRandom的方法

Random 在生成随机数时使用的是CAS 来解决线程安全问题的,如果多个线程操作时,效率是比较低的。可以使用 ThreadLocalRandom 来解决 Random 执行效率比较低的问题。

调用方法

public static void main(String[] args) {
		// 得到 ThreadLocalRandom 对象
		ThreadLocalRandom random = ThreadLocalRandom.current();
		for (int i = 0; i < 10; i++) {
		    // 生成 0-9 随机整数
		    int number = random.nextInt(10);
		    // 打印结果
		    System.out.println("生成随机数:" + number);
		}
	}

源码如下:

public int nextInt(int bound) {
    // 参数效验
    if (bound <= 0)
        throw new IllegalArgumentException(BadBound);
    // 根据当前线程中种子计算新种子
    int r = mix32(nextSeed());
    int m = bound - 1;
    // 根据新种子和 bound 计算随机数
    if ((bound & m) == 0) // power of two
        r &= m;
    else { // reject over-represented candidates
        for (int u = r >>> 1;
             u + m - (r = u % bound) < 0;
             u = mix32(nextSeed()) >>> 1)
            ;
    }
    return r;
}

final long nextSeed() {
    Thread t; long r; // read and update per-thread seed
    // 获取当前线程中 threadLocalRandomSeed 变量,然后在种子的基础上累加 GAMMA 值作为新种子
    // 再使用 UNSAFE.putLong 将新种子存放到当前线程的 threadLocalRandomSeed 变量中
    UNSAFE.putLong(t = Thread.currentThread(), SEED,
                   r = UNSAFE.getLong(t, SEED) + GAMMA); 
    return r;
}