歡迎關注公衆號【sharedCode】致力於主流中間件的源碼分析, 我的網站:https://www.shared-code.com/java
###ThreadLocalRandom介紹 ThreadLocalRandom是JDK1.7之後提供出來的一個隨機數生成工具類,性能比傳統的Math.random()更高。算法
性能比較
ThreadLocalRandom和Math.random()的性能比較,測試步驟以下:併發
public class Test { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(10); long startTime = System.currentTimeMillis(); //Math.random() 開啓10個線程 for (int i = 0; i < 10 ; i++){ Thread thread = new Thread(() -> { // 循環10000次 for (int j = 0; j < 10000 ; j++) { int x = (int) Math.random(); } countDownLatch.countDown(); }); thread.start(); } countDownLatch.await(); System.out.println("Math.random() 耗時 = " + (System.currentTimeMillis()-startTime)); long startTimeLocal = System.currentTimeMillis(); // ThreadLocalRandom 開啓10個線程 for (int i = 0; i < 10 ; i++){ Thread thread = new Thread(() -> { for (int j = 0; j < 10000 ; j++) { int x = ThreadLocalRandom.current().nextInt(); } }); thread.start(); } System.out.println("ThreadLocalRandom 耗時 = " + (System.currentTimeMillis()-startTimeLocal)); } }
測試結果以下:dom
第一次運行: Math.random() 耗時 = 112 ThreadLocalRandom 耗時 = 7 第二次運行: Math.random() 耗時 = 115 ThreadLocalRandom 耗時 = 15 第三次運行: Math.random() 耗時 = 94 ThreadLocalRandom 耗時 = 9 第四次運行: Math.random() 耗時 = 126 ThreadLocalRandom 耗時 = 16
經過上面的總結,咱們能夠很清晰看到,ThreadLocalRandom的性能優秀8-10倍高併發
源碼分析
public static ThreadLocalRandom current() { // 調用unsafe類裏面經過這個方法是從當前對象中獲取偏移offset的值 if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) // 等於0 ,說明當前線程沒有初始化,調用初始化方法。 localInit(); return instance; } /** * 該方法,主要是將當前線程,seed對象,放入到MAP結構中, */ static final void localInit() { int p = probeGenerator.addAndGet(PROBE_INCREMENT); int probe = (p == 0) ? 1 : p; // skip 0 long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); Thread t = Thread.currentThread(); UNSAFE.putLong(t, SEED, seed); UNSAFE.putInt(t, PROBE, probe); }
下面咱們看一下nextInt 這個方法。工具
public int nextInt() { return mix32(nextSeed()); } /** * 經過當前線程,拿到seed , 返回出去,用做隨機數計算 */ final long nextSeed() { Thread t; long r; // read and update per-thread seed UNSAFE.putLong(t = Thread.currentThread(), SEED, r = UNSAFE.getLong(t, SEED) + GAMMA); return r; }
隨機數的一個核心參數就是seed值的 , seed就是隨機算法中的種子 , 經過這個種子和一個未知的數"0xff51afd7ed558ccdL" 相乘,而後獲得一個未知的數,根據不一樣的類型截取不一樣的長度,如int, long 等數據類型。源碼分析
###Math.Random實現性能
protected int next(int bits) { long oldseed, nextseed; // 多個線程獲取到的是同一個seed對象 AtomicLong seed = this.seed; do { // 當前值 oldseed = seed.get(); // 下一個值經過某種計算出來 nextseed = (oldseed * multiplier + addend) & mask; // 經過CAS操做去更新,保證更新正確 } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits)); }
從上面很清晰的就看到了,原始的實現是全部的線程共享一個seed對象,雖然用的是CAS去更新,可是do while裏面仍是經過循環是重試,在高併發場景下是很耗費資源的。測試
總結:網站
ThreadLocalRandom是經過每一個線程獲取本身的seed對象,不存在線程之間的資源競爭,所以速度那是快的飛起。