1. 程式人生 > 其它 >java中Random實現原理

java中Random實現原理

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演算法。

參考

java.util.Random 實現原理
第3章 Java併發包中ThreadLocalRandom類原理剖析