🟨 ThreadLocalRandom

吞佛童子2022年10月10日
  • Java
  • Concurrency
大约 3 分钟

🟨 ThreadLocalRandom

1. 类注释

  • Random 类每次获取随机数时,通过 CAS 修改 seed 值,多线程并发下,只能有一个线程成功修改 seed 值,其余线程无限重试
    • 冲突高时,影响并发效率
  • ThreadLocalRandom 类每次获取随机数时,通过 Unsafe 类直接修改 seed 值,各线程下 seed 互不影响,只与 当前线程 有关,因此也无需通过 CAS 设置
    • 高并发下,不影响并发效率

使用

ThreadLocalRandom rand = ThreadLocalRandom.current(); // 获取当前线程对应的 ThreadLocalRandom 单例
int rint = rand.nextInt(10); // 生成 [0, 10) 的随机数
double rdou = rand.nextDouble(3.0, 29); // 生成 [3.0, 29) 的随机数

2. 源码

public class ThreadLocalRandom extends Random {
    // 原子类 -- 初始化用
    private static final AtomicInteger probeGenerator = new AtomicInteger();
    private static final int PROBE_INCREMENT = 0x9e3779b9; // 初始化时,原子类的每次增长值
    
    private static final AtomicLong seeder = new AtomicLong(initialSeed()); // 原子类 -- 种子值
    private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL; // 种子的增长值
    
    boolean initialized; // 仅在单例初始化期间使用的字段。构造函数完成时为真。
    
    // 私有构造方法,只能通过 ThreadLocalRandom.current() 得到 -- 单例模式
    private ThreadLocalRandom() {
        initialized = true; // false during super() call
    }
    
    static final ThreadLocalRandom instance = new ThreadLocalRandom();
    
    // 提供的外部访问单例
    public static ThreadLocalRandom current() {
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            localInit();
        return instance;
    }
    
    // 为当前线程初始化线程字段
    // 仅在 Thread.threadLocalRandomProbe 为零时调用,表示需要生成线程本地种子值。
    // 即使初始化是纯线程本地的,我们也需要依赖(静态)原子生成器来初始化值。
    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 方法中获取随机数时,用到
    // seed 的生成只与 当前线程有关,因此 多线程下无冲突
    final long nextSeed() {
        Thread t = Thread.currentThread(); 
        long r = UNSAFE.getLong(t, SEED) + GAMMA; 
        UNSAFE.putLong(t, SEED, r);
        return r;
    }
    
    // ---------- 提供的 外部方法 ---------------
    // 返回 [origin, bound) 随机 int 值 --> ThreadLocalRandom 独有
    public int nextInt(int origin, int bound) {
        if (origin >= bound)
            throw new IllegalArgumentException(BadRange);
        return internalNextInt(origin, bound);
    }
    // 返回 [0, bound) 随机 int 值 --> 重写 Random 方法
    public int nextInt(int bound) {
        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)
                ;
        }
        return r;
    }
    // 返回 [0, 1) 随机 int 值 --> 重写 Random 方法
    public int nextInt() {
        return mix32(nextSeed());
    }
    
    public long nextLong() {
        return mix64(nextSeed());
    }
    
    // ThreadLocalRandom 独有
    public double nextDouble(double bound) {
        if (!(bound > 0.0))
            throw new IllegalArgumentException(BadBound);
        double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
        return (result < bound) ?  result : // correct for rounding
            Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
    }
    
    public float nextFloat() {
        return (mix32(nextSeed()) >>> 8) * FLOAT_UNIT;
    }
    
    public boolean nextBoolean() {
        return mix32(nextSeed()) < 0;
    }
}

3. Random

public class Random implements java.io.Serializable {
    // 种子值
    private final AtomicLong seed;
    private static final long multiplier = 0x5DEECE66DL;
    private static final long mask = (1L << 48) - 1;
    
    // 构造函数 - 种子初始值与 某个固定值 & 当前时间 有关
    public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }
    // 构造函数 - 指定 seed 初始值
    public Random(long seed) {
        if (getClass() == Random.class)
            this.seed = new AtomicLong(initialScramble(seed));
        else {
            this.seed = new AtomicLong();
            setSeed(seed);
        }
    }
    
    // 生成 seed -- 固定值
    private static long seedUniquifier() {
        for (;;) {
            long current = seedUniquifier.get();
            long next = current * 181783497276652981L;
            if (seedUniquifier.compareAndSet(current, next))
                return next;
        }
    }
    // 用于 初始化 时,生成 seed 的一部分 --> 固定值
    private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
    // 根据指定 seed 初始值生成 满足条件的 seed
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    }
    // 根据指定 seed 生成 随机生成器的 seed
    synchronized public void setSeed(long seed) {
        this.seed.set(initialScramble(seed));
        haveNextNextGaussian = false;
    }
    
    // -------------- 公开方法 -----------------------
    //
    public int nextInt() {
        return next(32);
    }
    
    //
    public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);

        int r = next(31);
        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;
    }
    
    //
    public double nextDouble() {
        return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
    }
    
    // 生产下一个随机数 -- 可以看到使用 CAS 保证每次只有一个线程能够成功进入,其他线程只能无限失败重试
    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));
    }
}
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou