Random的升級版---ThreadlocalRandom

Random在日常開發過程中還是挺常用的,用於生成隨機值;

 Random random = new Random();
 random.nextInt(100);
 /**
 *nextInt(100)實際調用的方法是next(int bits)
 *
 */
 protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
            //這裏使用CAS,保證多線程情況下只有一個線程執行成功
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

從上面的源碼看,可知Random生成隨機數是線程安全的,但是在多線程併發的情況下,自旋操作會增加CPU的負擔,而ThreadlocalRandom能夠有效的降低這種壓力;從名稱上大致能夠看出ThreadlocalRandom的實現原理與Threadlocal相似,多個線程相互隔離的。下面是ThreadlocalRandom的next()的實現。

public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
        int r = mix32(nextSeed());
        int m = bound - 1;
        if ((bound & m) == 0) //bound是2次冪
            r &= m;
        else { // 不是2次冪
            for (int u = r >>> 1;
                 u + m - (r = u % bound) < 0;
                 u = mix32(nextSeed()) >>> 1) ;
        }
        return r;
    }

在ThreadlocalRandom裏面重寫了next方法,並未沿用Random中的cas原子性操作,隨機數生成的種子seed並未放在當前類中,而是通過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常量初始化在創建實例的時候;ThreadlocalRandom的實例獲取方式

ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();

通過靜態代碼塊進行一些變量的初始化。

static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            //獲取線程Thread中的threadLocalRandomSeed常量,作爲seed
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

在ThreadLocalRandom實例化的時候將靜態代碼塊中初始化的參數寫進ThreadLocalRandom 類中;

public static ThreadLocalRandom current() {
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            localInit();
        return instance;
    }

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);
    }

在Thread中

 /** The current seed for a ThreadLocalRandom */
    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

    /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

    /** Secondary seed isolated from public ThreadLocalRandom sequence */
    @sun.misc.Contended("tlr")
    int threadLocalRandomSecondarySeed;

其中加這個註解的原因@sun.misc.Contended,就是避免僞共享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章