Random类的实现

  1. public class Code01_RandomTest {
  2. public static void main(String[] args) {
  3. Random random = new Random();
  4. for (int i = 0; i < 10; i++) {
  5. System.out.println(random.nextInt(15));
  6. }
  7. }
  8. }
// 返回[0,bound)内的随机整数

public int nextInt(int bound) {
    // 如果给点的边界小于0 ,抛出异常
    if (bound <= 0)
        throw new IllegalArgumentException(BadBound);

    // 根据老的seed值生成新的seed值
    int r = next(31);

    // 根据新的seed值生成随机数
    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;
}
// 根据老的seed值生成新的seed值  CAS操作

protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;  // seed,原子变量
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));

    return (int)(nextseed >>> (48 - bits));
}

每个Random示例都有一个原子性的seed变量用于记录当前的seed值 ,当要乘车新的随机数是根据当前seed值计算新的seed值并更新原子性变量。
image.png
在多线程下使用单个Random实例生成随机数,当多线程同时计算新的seed值是。多线程竞争同一个原子变量的更新操作,因为是CAS操作,因此只会有一个线程成功,其他线程自旋重试,降低的并发性能。因此产生了ThreadLocalRandom类。

ThreadLocalRandom

每个线程都维护一个seed变量,则每个线程生成随机数时都根据自己老的seed计算新的种子,并使用新seed更新老的种子,再根据新seed计算随机数, 就不会存在竞争问题了,这会大大提高并发性能
image.png
image.png

  • ThreadLocalRandom 中并没有存放具体的seed,具体的seed存放在具体的调用线程的threadLocalRandomSeed 变量里面。
  • 当线程调用ThreadLoca!Random 的curentent 方法时, ThreadLoca!Random 负责初始化调用线程的threadLocalRandomSeed 变量, 也就是初始化seed。
  • 当调用ThreadLocalRandom 的nextlnt 方法时,实际上是获取当前线程的threadLocalRandomS eed 变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLoca!RandomSeed 变量,而后再根据新种子并使用具体算法计算随机数。
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;

// 静态代码块
static {
    try {
        // 获取Unsafe实例
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> tk = Thread.class;

        // 获取Thread类里面threadLocalRandomSeed交量在Thread 实例里面的偏移量
        SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));

        // 获取Thread类里面threadLocalRan domProbe 变量在Thread 实例里面的偏移堂
        PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));
        // 获取Thread类里面threadLocalRandomSecondarySeed交量在Thread实例里面的偏移量,
        //  这个值在后面讲解LongAdder 时会用到
        SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));

    } catch (Exception e) {
        throw new Error(e);
    }
}
public class Code02_ThreadLocalRandomTest {
    public static void main(String[] args) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        for (int i = 0; i < 10; i++) {
            System.out.println(threadLocalRandom.nextInt(15));
        }
    }
}
public static ThreadLocalRandom current() {
    // 第一个调用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);
}
public int nextInt(int bound) {
    // 如果给点的边界小于0 ,抛出异常
    if (bound <= 0)
        throw new IllegalArgumentException(BadBound);
    int r = mix32(nextSeed());
    int m = bound - 1;
    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)
            ;
    }
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;
    }

用r = UNSAFE.getLo吨(t, SEED) 获取当前线程中threadLocalRandomSeed 变量的值, 然后在种子的基础上累加GAMMA 值作为新种子,而后使用UNSAFE 的putLong 方法把新种子放入当前线程的threadLoca!RandomSeed 变量中。